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 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 } 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';
@ -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,
slideIn = 'Left',
command = () => {},
} = {}) => {
if (!notif)
return;
HasNotifs.value = Notifications.notifications.length > 0;
const BlockedApps = [
'Spotify',
];
@ -100,9 +112,11 @@ export default ({
return;
}
return Gesture({
// Init notif
const notifWidget = Gesture({
maxOffset: 200,
command: () => command(),
slideIn,
properties: [
['hovered', false],
['id', notif.id],
@ -115,77 +129,93 @@ export default ({
if (w._hovered)
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 { 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 EventBox from '../misc/cursorbox.js';
// Needs to be wrapped to still have onHover when disabled
const ClearButton = () => EventBox({
child: Button({
onPrimaryClickRelease: button => {
button._popups.children.forEach(ch => {
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;
}]],
onPrimaryClickRelease: () => Notifications.clear(),
binds: [['sensitive', HasNotifs]],
child: Box({
children: [
Label('Clear '),
@ -66,16 +37,21 @@ const Header = () => Box({
],
});
const NotificationList = Box({
const NotificationList = () => Box({
vertical: true,
valign: 'start',
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(),
});
@ -90,6 +66,7 @@ const NotificationList = Box({
const NewNotif = Notification({
notif,
slideIn: 'Right',
command: () => notif.close(),
});
@ -103,23 +80,17 @@ const NotificationList = Box({
[Notifications, (box, id) => {
for (const ch of box.children) {
if (ch._id == id) {
ch.child.setStyle(ch.child._slideRight);
ch.sensitive = false;
timeout(500, () => box.remove(ch));
ch.slideAway('Right');
return;
}
}
}, 'closed'],
[Notifications, box => box.visible = Notifications.notifications.length > 0],
],
});
const Placeholder = () => Revealer({
transition: 'crossfade',
connections: [[Notifications, box => {
box.revealChild = Notifications.notifications.length === 0;
}]],
binds: [['revealChild', HasNotifs, 'value', value => !value]],
child: Box({
className: 'placeholder',
vertical: true,
@ -149,7 +120,7 @@ const NotificationCenterWidget = () => Box({
className: 'notification-list',
vertical: true,
children: [
NotificationList,
NotificationList(),
Placeholder(),
],
}),

View file

@ -7,6 +7,7 @@ const display = Gdk.Display.get_default();
export default ({
slideIn = 'Left',
maxOffset = 150,
startMargin = 0,
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;
if (widget.window)
@ -94,9 +96,9 @@ export default ({
}, 'drag-update'],
[gesture, self => {
// ?
// Make it slide in on init
if (!self._ready) {
self.setStyle(slideLeft);
self.setStyle(slideIn === 'Left' ? slideLeft : slideRight);
timeout(500, () => self.setStyle(`${TRANSITION} margin: unset; opacity: 1;`));
timeout(1000, () => self._ready = true);

View file

@ -1,13 +1,12 @@
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
import { Box, Revealer, Window } from 'resource:///com/github/Aylur/ags/widget.js';
import { interval, timeout } from 'resource:///com/github/Aylur/ags/utils.js';
import { Box, Window } 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 { Notification } from './base.js';
// TODO: clean up code
const Popups = () => Box({
vertical: true,
properties: [
@ -21,6 +20,7 @@ const Popups = () => Box({
});
if (NewNotif) {
// use this instead of add to put it at the top
box.pack_end(NewNotif, false, false, 0);
box.show_all();
}
@ -30,28 +30,21 @@ const Popups = () => Box({
['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.child.setStyle(ch.child._slideLeft);
ch.sensitive = false;
timeout(400, () => {
ch.child.setStyle(ch.child._squeezeLeft);
timeout(500, () => box.remove(ch));
});
ch.slideAway('Left');
return;
}
// If notif is hovered, delay close
else if (ch._hovered) {
ch.interval = interval(2000, () => {
if (!ch._hovered) {
if (ch.interval) {
ch.child.setStyle(ch.child._slideLeft);
ch.sensitive = false;
timeout(400, () => {
ch.child.setStyle(ch.child._squeezeLeft);
timeout(500, () => box.remove(ch));
});
GLib.source_remove(ch.interval);
ch.interval = undefined;
}
if (!ch._hovered && ch.interval) {
ch.slideAway('Left');
GLib.source_remove(ch.interval);
ch.interval = undefined;
return;
}
});
}
@ -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({
name: 'notifications',
anchor: ['top', 'left'],
child: PopupList(),
child: Box({
style: `min-height:1px;
min-width:1px;
padding: 1px;`,
children: [Popups()],
}),
});