fix(ags notifs): refactor and make notifs work with latest ags

This commit is contained in:
matt1432 2023-11-06 20:10:12 -05:00
parent 6a40c0141d
commit b2e4c84545
5 changed files with 189 additions and 220 deletions

View file

@ -4,8 +4,8 @@ import { exec } from 'resource:///com/github/Aylur/ags/utils.js';
import Setup from './js/setup.js'; import Setup from './js/setup.js';
import Powermenu from './js/powermenu.js'; import Powermenu from './js/powermenu.js';
import * as Bar from './js/bar/main.js'; import * as Bar from './js/bar/main.js';
//import NotifCenter from './js/notifications/center.js'; import NotifCenter from './js/notifications/center.js';
//import NotifPopups from './js/notifications/popup.js'; import NotifPopups from './js/notifications/popup.js';
import Calendar from './js/date.js'; import Calendar from './js/date.js';
import QuickSettings from './js/quick-settings/main.js'; import QuickSettings from './js/quick-settings/main.js';
//import Overview from './js/overview/main.js'; //import Overview from './js/overview/main.js';
@ -18,7 +18,6 @@ exec(`sassc ${scss} ${css}`);
Setup(); Setup();
// FIXME: notification and overview stuff are bugged as of this ags commit
export default { export default {
style: css, style: css,
notificationPopupTimeout: 5000, notificationPopupTimeout: 5000,
@ -34,7 +33,7 @@ export default {
windows: [ windows: [
AppLauncher(), AppLauncher(),
Calendar(), Calendar(),
//NotifCenter(), NotifCenter(),
//Overview(), //Overview(),
Powermenu(), Powermenu(),
QuickSettings(), QuickSettings(),
@ -43,6 +42,6 @@ export default {
Bar.BgGradient(), Bar.BgGradient(),
Corners.Bottomleft(), Corners.Bottomleft(),
Corners.Bottomright(), Corners.Bottomright(),
//NotifPopups(), NotifPopups(),
], ],
}; };

View file

@ -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 Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
import Variable from 'resource:///com/github/Aylur/ags/variable.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 { 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'; import GLib from 'gi://GLib';
@ -48,15 +48,17 @@ const NotificationIcon = notif => {
return EventBox({ return EventBox({
onPrimaryClickRelease: iconCmd, onPrimaryClickRelease: iconCmd,
child: Box({ child: Box({
valign: 'start', vpack: 'start',
hexpand: false, hexpand: false,
className: 'icon img', className: 'icon img',
style: `background-image: url("${notif.image}"); css: `
background-size: contain; background-image: url("${notif.image}");
background-repeat: no-repeat; background-size: contain;
background-position: center; background-repeat: no-repeat;
min-width: 78px; background-position: center;
min-height: 78px;`, min-width: 78px;
min-height: 78px;
`,
}), }),
}); });
} }
@ -73,16 +75,16 @@ const NotificationIcon = notif => {
return EventBox({ return EventBox({
onPrimaryClickRelease: iconCmd, onPrimaryClickRelease: iconCmd,
child: Box({ child: Box({
valign: 'start', vpack: 'start',
hexpand: false, hexpand: false,
className: 'icon', className: 'icon',
style: `min-width: 78px; css: `min-width: 78px;
min-height: 78px;`, min-height: 78px;`,
children: [Icon({ children: [Icon({
icon, size: 58, icon, size: 58,
halign: 'center', hpack: 'center',
hexpand: true, hexpand: true,
valign: 'center', vpack: 'center',
vexpand: true, vexpand: true,
})], })],
}), }),
@ -114,37 +116,11 @@ export const Notification = ({
// Init notif // Init notif
const notifWidget = Gesture({ const notifWidget = Gesture({
maxOffset: 200, command,
command: () => command(),
slideIn, slideIn,
properties: [ id: notif.id,
['hovered', false],
['id', notif.id],
],
onHover: w => {
if (!w._hovered)
w._hovered = true;
},
onHoverLost: w => {
if (w._hovered)
w._hovered = false;
},
}); });
// 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 // Add body to notif
notifWidget.child.add(Box({ notifWidget.child.add(Box({
className: `notification ${notif.urgency}`, className: `notification ${notif.urgency}`,
@ -177,14 +153,14 @@ export const Notification = ({
}), }),
Label({ Label({
className: 'time', className: 'time',
valign: 'start', vpack: 'start',
label: setTime(notif.time), label: setTime(notif.time),
}), }),
EventBox({ EventBox({
reset: false, reset: false,
child: Button({ child: Button({
className: 'close-button', className: 'close-button',
valign: 'start', vpack: 'start',
onClicked: () => notif.close(), onClicked: () => notif.close(),
child: Icon('window-close-symbolic'), child: Icon('window-close-symbolic'),
}), }),

View file

@ -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 { Button, Label, Box, Icon, Scrollable, Revealer } from 'resource:///com/github/Aylur/ags/widget.js';
import { Notification, HasNotifs } from './base.js'; import { Notification, HasNotifs } from './base.js';
import PopupWindow from '../misc/popup.js'; import PopupWindow from '../misc/popup.js';
import EventBox from '../misc/cursorbox.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 // Needs to be wrapped to still have onHover when disabled
const ClearButton = () => EventBox({ const ClearButton = () => EventBox({
child: Button({ 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({ const Placeholder = () => Revealer({
transition: 'crossfade', transition: 'crossfade',
binds: [['revealChild', HasNotifs, 'value', value => !value]], binds: [['revealChild', HasNotifs, 'value', value => !value]],
child: Box({ child: Box({
className: 'placeholder', className: 'placeholder',
vertical: true, vertical: true,
valign: 'center', vpack: 'center',
halign: 'center', hpack: 'center',
vexpand: true, vexpand: true,
hexpand: true, hexpand: true,
children: [ children: [
@ -133,6 +128,6 @@ const NotificationCenterWidget = () => Box({
export default () => PopupWindow({ export default () => PopupWindow({
name: 'notification-center', name: 'notification-center',
anchor: ['top', 'right'], anchor: ['top', 'right'],
margin: [6, 60, 0, 0], margins: [6, 60, 0, 0],
child: NotificationCenterWidget(), child: NotificationCenterWidget(),
}); });

View file

@ -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 { Box, EventBox } from 'resource:///com/github/Aylur/ags/widget.js';
import { timeout } from 'resource:///com/github/Aylur/ags/utils.js'; import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
import { HasNotifs } from './base.js';
import Gtk from 'gi://Gtk'; import Gtk from 'gi://Gtk';
import Gdk from 'gi://Gdk'; import Gdk from 'gi://Gdk';
const display = Gdk.Display.get_default(); const display = Gdk.Display.get_default();
export default ({ export default ({
id,
slideIn = 'Left', slideIn = 'Left',
maxOffset = 150, maxOffset = 200,
startMargin = 0, startMargin = 0,
endMargin = 300, endMargin = 300,
command = () => {}, command = () => {},
onHover = () => {},
onHoverLost = () => {},
child = '',
children = [],
properties = [[]],
...props ...props
}) => { }) => {
const widget = EventBox({ const widget = EventBox({
...props, ...props,
properties: [
['dragging', false],
...properties,
],
onHover: self => { onHover: self => {
self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab')); self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
onHover(self); if (!self._hovered)
self._hovered = true;
}, },
onHoverLost: self => { onHoverLost: self => {
self.window.set_cursor(null); 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 gesture = Gtk.GestureDrag.new(widget);
const TRANSITION = 'transition: margin 0.5s ease, opacity 0.5s ease;'; const TRANSITION = 'transition: margin 0.5s ease, opacity 0.5s ease;';
@ -48,32 +51,45 @@ export default ({
margin-right: -${Number(maxOffset + endMargin)}px; 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 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 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({ widget.add(Box({
properties: [ css: squeezeLeft,
['slideLeft', slideLeft],
['squeezeLeft', squeezeLeft],
['slideRight', slideRight],
['squeezeRight', squeezeRight],
['ready', false],
],
children: [
...children,
child,
],
style: squeezeLeft,
connections: [ connections: [
// When dragging
[gesture, self => { [gesture, self => {
var offset = gesture.get_offset()[1]; var offset = gesture.get_offset()[1];
// Slide right // Slide right
if (offset >= 0) { if (offset >= 0) {
self.setStyle(` self.setCss(`
margin-top: 0px; margin-bottom: 0px; opacity: 1; transition: none;
margin-left: ${Number(offset + startMargin)}px; margin-left: ${Number(offset + startMargin)}px;
margin-right: -${Number(offset + startMargin)}px; margin-right: -${Number(offset + startMargin)}px;
`); `);
@ -82,26 +98,31 @@ export default ({
// Slide left // Slide left
else { else {
offset = Math.abs(offset); 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-right: ${Number(offset + startMargin)}px;
margin-left: -${Number(offset + startMargin)}px; margin-left: -${Number(offset + startMargin)}px;
`); `);
} }
// Put a threshold on if a click is actually dragging // Put a threshold on if a click is actually dragging
self.get_parent()._dragging = Math.abs(offset) > 10; widget._dragging = Math.abs(offset) > 10;
widget.window?.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing'));
if (widget.window)
widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing'));
}, 'drag-update'], }, 'drag-update'],
// On drag end
[gesture, self => { [gesture, self => {
// Make it slide in on init // Make it slide in on init
if (!self._ready) { if (!widget.ready) {
self.setStyle(slideIn === 'Left' ? slideLeft : slideRight); // 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(500, () => {
timeout(1000, () => self._ready = true); // Then we got to center
self.setCss(defaultStyle);
timeout(500, () => widget.ready = true);
});
return; return;
} }
@ -109,35 +130,16 @@ export default ({
// If crosses threshold after letting go, slide away // If crosses threshold after letting go, slide away
if (Math.abs(offset) > maxOffset) { if (Math.abs(offset) > maxOffset) {
// Slide away right if (offset > 0)
if (offset > 0) { widget.slideAway('Right');
// Disable inputs during animation else
widget.sensitive = false; widget.slideAway('Left');
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();
});
} }
else { else {
self.setStyle(`${TRANSITION} margin: unset; opacity: 1;`); self.setCss(defaultStyle);
if (widget.window) widget.window?.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
self.get_parent()._dragging = false; widget._dragging = false;
} }
}, 'drag-end'], }, 'drag-end'],

View file

@ -1,71 +1,68 @@
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; 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 { interval } from 'resource:///com/github/Aylur/ags/utils.js';
import GLib from 'gi://GLib'; import GLib from 'gi://GLib';
import { Notification } from './base.js'; import { Notification } from './base.js';
import PopupWindow from '../misc/popup.js';
const Popups = () => Box({ const addPopup = (box, id) => {
vertical: true, if (!id)
properties: [ return;
['notify', (box, id) => {
if (id) {
const notif = Notifications.getNotification(id);
const NewNotif = Notification({ const notif = Notifications.getNotification(id);
notif,
command: () => notif.dismiss(),
});
if (NewNotif) { const NewNotif = Notification({
// use this instead of add to put it at the top notif,
box.pack_end(NewNotif, false, false, 0); command: () => notif.dismiss(),
box.show_all(); });
}
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) => { export default () => PopupWindow({
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({
name: 'notifications', name: 'notifications',
anchor: ['top', 'left'], anchor: ['top', 'left'],
visible: true,
transition: 'none',
closeOnUnfocus: 'none',
child: Box({ child: Box({
style: `min-height:1px; vertical: true,
min-width:1px; connections: [
padding: 1px;`, [Notifications, (box, id) => addPopup(box, id), 'notified'],
children: [Popups()], [Notifications, (box, id) => handleDismiss(box, id), 'dismissed'],
[Notifications, (box, id) => handleDismiss(box, id, true), 'closed'],
],
}), }),
}); });