refactor(ags notifs): clarify code, add way to slide in from right, fix clear button, etc.

This commit is contained in:
matt1432 2023-11-03 15:28:08 -04:00
parent ed3f8f6eb1
commit ad02510b10
4 changed files with 144 additions and 157 deletions

View file

@ -1,6 +1,8 @@
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js'; import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
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 { Box, Icon, Label, Button } from 'resource:///com/github/Aylur/ags/widget.js';
import { lookUpIcon, execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; import { lookUpIcon, execAsync, timeout } from 'resource:///com/github/Aylur/ags/utils.js';
import GLib from 'gi://GLib'; import GLib from 'gi://GLib';
@ -87,10 +89,20 @@ const NotificationIcon = notif => {
}); });
}; };
export default ({ // Make a variable to connect to for Widgets
// to know when there are notifs or not
export const HasNotifs = Variable(false);
export const Notification = ({
notif, notif,
slideIn = 'Left',
command = () => {}, command = () => {},
} = {}) => { } = {}) => {
if (!notif)
return;
HasNotifs.value = Notifications.notifications.length > 0;
const BlockedApps = [ const BlockedApps = [
'Spotify', 'Spotify',
]; ];
@ -100,9 +112,11 @@ export default ({
return; return;
} }
return Gesture({ // Init notif
const notifWidget = Gesture({
maxOffset: 200, maxOffset: 200,
command: () => command(), command: () => command(),
slideIn,
properties: [ properties: [
['hovered', false], ['hovered', false],
['id', notif.id], ['id', notif.id],
@ -115,77 +129,93 @@ export default ({
if (w._hovered) if (w._hovered)
w._hovered = false; w._hovered = false;
}, },
child: Box({
className: `notification ${notif.urgency}`,
vexpand: false,
// Notification
child: Box({
vertical: true,
children: [
// Content
Box({
children: [
NotificationIcon(notif),
Box({
hexpand: true,
vertical: true,
children: [
// Top of Content
Box({
children: [
Label({
className: 'title',
xalign: 0,
justification: 'left',
hexpand: true,
maxWidthChars: 24,
truncate: 'end',
wrap: true,
label: notif.summary,
useMarkup: notif.summary.startsWith('<'),
}),
Label({
className: 'time',
valign: 'start',
label: setTime(notif.time),
}),
EventBox({
reset: false,
child: Button({
className: 'close-button',
valign: 'start',
onClicked: () => notif.close(),
child: Icon('window-close-symbolic'),
}),
}),
],
}),
Label({
className: 'description',
hexpand: true,
useMarkup: true,
xalign: 0,
justification: 'left',
label: notif.body,
wrap: true,
}),
],
}),
],
}),
// Actions
Box({
className: 'actions',
children: notif.actions.map(action => Button({
className: 'action-button',
onClicked: () => notif.invoke(action.id),
hexpand: true,
child: Label(action.label),
})),
}),
],
}),
}),
}); });
// 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}`,
vexpand: false,
// Notification
child: Box({
vertical: true,
children: [
// Content
Box({
children: [
NotificationIcon(notif),
Box({
hexpand: true,
vertical: true,
children: [
// Top of Content
Box({
children: [
Label({
className: 'title',
xalign: 0,
justification: 'left',
hexpand: true,
maxWidthChars: 24,
truncate: 'end',
wrap: true,
label: notif.summary,
useMarkup: notif.summary.startsWith('<'),
}),
Label({
className: 'time',
valign: 'start',
label: setTime(notif.time),
}),
EventBox({
reset: false,
child: Button({
className: 'close-button',
valign: 'start',
onClicked: () => notif.close(),
child: Icon('window-close-symbolic'),
}),
}),
],
}),
Label({
className: 'description',
hexpand: true,
useMarkup: true,
xalign: 0,
justification: 'left',
label: notif.body,
wrap: true,
}),
],
}),
],
}),
// Actions
Box({
className: 'actions',
children: notif.actions.map(action => Button({
className: 'action-button',
onClicked: () => notif.invoke(action.id),
hexpand: true,
child: Label(action.label),
})),
}),
],
}),
}));
return notifWidget;
}; };

View file

@ -1,45 +1,16 @@
import App from 'resource:///com/github/Aylur/ags/app.js';
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
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 { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
import Notification 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';
// Needs to be wrapped to still have onHover when disabled
const ClearButton = () => EventBox({ const ClearButton = () => EventBox({
child: Button({ child: Button({
onPrimaryClickRelease: button => { onPrimaryClickRelease: () => Notifications.clear(),
button._popups.children.forEach(ch => { binds: [['sensitive', HasNotifs]],
ch.child.setStyle(ch.child._slideLeft);
ch.sensitive = false;
});
button._notifList.children.forEach(ch => {
if (ch.child) {
ch.child.setStyle(ch.child._slideRight);
ch.sensitive = false;
}
timeout(500, () => {
button._notifList.remove(ch);
Notifications.clear();
});
});
},
properties: [
['notifList'],
['popups'],
],
connections: [[Notifications, button => {
if (!button._notifList)
button._notifList = NotificationList;
if (!button._popups)
button._popups = App.getWindow('notifications').child.children[0].child;
button.sensitive = Notifications.notifications.length > 0;
}]],
child: Box({ child: Box({
children: [ children: [
Label('Clear '), Label('Clear '),
@ -66,16 +37,21 @@ const Header = () => Box({
], ],
}); });
const NotificationList = Box({ const NotificationList = () => Box({
vertical: true, vertical: true,
valign: 'start',
vexpand: true, vexpand: true,
valign: 'start',
binds: [['visible', HasNotifs]],
connections: [ connections: [
[Notifications, (box, id) => { [Notifications, (box, id) => {
if (box.children.length == 0) { if (box.children.length == 0) {
for (const notif of Notifications.notifications) { for (const notif of Notifications.notifications) {
if (!notif)
return;
const NewNotif = Notification({ const NewNotif = Notification({
notif, notif,
slideIn: 'Right',
command: () => notif.close(), command: () => notif.close(),
}); });
@ -90,6 +66,7 @@ const NotificationList = Box({
const NewNotif = Notification({ const NewNotif = Notification({
notif, notif,
slideIn: 'Right',
command: () => notif.close(), command: () => notif.close(),
}); });
@ -103,23 +80,17 @@ const NotificationList = Box({
[Notifications, (box, id) => { [Notifications, (box, id) => {
for (const ch of box.children) { for (const ch of box.children) {
if (ch._id == id) { if (ch._id == id) {
ch.child.setStyle(ch.child._slideRight); ch.slideAway('Right');
ch.sensitive = false;
timeout(500, () => box.remove(ch));
return; return;
} }
} }
}, 'closed'], }, 'closed'],
[Notifications, box => box.visible = Notifications.notifications.length > 0],
], ],
}); });
const Placeholder = () => Revealer({ const Placeholder = () => Revealer({
transition: 'crossfade', transition: 'crossfade',
connections: [[Notifications, box => { binds: [['revealChild', HasNotifs, 'value', value => !value]],
box.revealChild = Notifications.notifications.length === 0;
}]],
child: Box({ child: Box({
className: 'placeholder', className: 'placeholder',
vertical: true, vertical: true,
@ -149,7 +120,7 @@ const NotificationCenterWidget = () => Box({
className: 'notification-list', className: 'notification-list',
vertical: true, vertical: true,
children: [ children: [
NotificationList, NotificationList(),
Placeholder(), Placeholder(),
], ],
}), }),

View file

@ -7,6 +7,7 @@ const display = Gdk.Display.get_default();
export default ({ export default ({
slideIn = 'Left',
maxOffset = 150, maxOffset = 150,
startMargin = 0, startMargin = 0,
endMargin = 300, endMargin = 300,
@ -87,6 +88,7 @@ export default ({
`); `);
} }
// Put a threshold on if a click is actually dragging
self.get_parent()._dragging = Math.abs(offset) > 10; self.get_parent()._dragging = Math.abs(offset) > 10;
if (widget.window) if (widget.window)
@ -94,9 +96,9 @@ export default ({
}, 'drag-update'], }, 'drag-update'],
[gesture, self => { [gesture, self => {
// ? // Make it slide in on init
if (!self._ready) { if (!self._ready) {
self.setStyle(slideLeft); self.setStyle(slideIn === 'Left' ? slideLeft : slideRight);
timeout(500, () => self.setStyle(`${TRANSITION} margin: unset; opacity: 1;`)); timeout(500, () => self.setStyle(`${TRANSITION} margin: unset; opacity: 1;`));
timeout(1000, () => self._ready = true); timeout(1000, () => self._ready = true);

View file

@ -1,13 +1,12 @@
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
import { Box, Revealer, Window } from 'resource:///com/github/Aylur/ags/widget.js'; import { Box, Window } from 'resource:///com/github/Aylur/ags/widget.js';
import { interval, timeout } 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';
// TODO: clean up code
const Popups = () => Box({ const Popups = () => Box({
vertical: true, vertical: true,
properties: [ properties: [
@ -21,6 +20,7 @@ const Popups = () => Box({
}); });
if (NewNotif) { if (NewNotif) {
// use this instead of add to put it at the top
box.pack_end(NewNotif, false, false, 0); box.pack_end(NewNotif, false, false, 0);
box.show_all(); box.show_all();
} }
@ -30,28 +30,21 @@ const Popups = () => Box({
['dismiss', (box, id, force = false) => { ['dismiss', (box, id, force = false) => {
for (const ch of box.children) { for (const ch of box.children) {
if (ch._id == id) { if (ch._id == id) {
// If notif isn't hovered or was closed, slide away
if (!ch._hovered || force) { if (!ch._hovered || force) {
ch.child.setStyle(ch.child._slideLeft); ch.slideAway('Left');
ch.sensitive = false;
timeout(400, () => {
ch.child.setStyle(ch.child._squeezeLeft);
timeout(500, () => box.remove(ch));
});
return; return;
} }
// If notif is hovered, delay close
else if (ch._hovered) { else if (ch._hovered) {
ch.interval = interval(2000, () => { ch.interval = interval(2000, () => {
if (!ch._hovered) { if (!ch._hovered && ch.interval) {
if (ch.interval) { ch.slideAway('Left');
ch.child.setStyle(ch.child._slideLeft);
ch.sensitive = false; GLib.source_remove(ch.interval);
timeout(400, () => { ch.interval = undefined;
ch.child.setStyle(ch.child._squeezeLeft); return;
timeout(500, () => box.remove(ch));
});
GLib.source_remove(ch.interval);
ch.interval = undefined;
}
} }
}); });
} }
@ -66,22 +59,13 @@ const Popups = () => Box({
], ],
}); });
const PopupList = ({ transition = 'none' } = {}) => Box({
className: 'notifications-popup-list',
style: 'padding: 1px',
children: [
Revealer({
transition,
connections: [[Notifications, self => {
self.revealChild = self.child.children.length > 0;
}]],
child: Popups(),
}),
],
});
export default () => Window({ export default () => Window({
name: 'notifications', name: 'notifications',
anchor: ['top', 'left'], anchor: ['top', 'left'],
child: PopupList(), child: Box({
style: `min-height:1px;
min-width:1px;
padding: 1px;`,
children: [Popups()],
}),
}); });