add notification center example

This commit is contained in:
Aylur
2023-08-20 19:18:01 +02:00
parent 09cbb1e579
commit 645af14730
6 changed files with 352 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
const { Window, Box, Label } = ags.Widget;
import {
NotificationList, DNDSwitch, ClearButton, PopupList,
} from './widgets.js';
const Header = () => Box({
className: 'header',
children: [
Label('Do Not Disturb'),
DNDSwitch(),
Box({ hexpand: true }),
ClearButton(),
],
});
const NotificationCenter = () => Window({
name: 'notification-center',
anchor: 'right top bottom',
popup: true,
focusable: true,
child: Box({
vertical: true,
children: [
Header(),
NotificationList(),
],
}),
});
const NotificationsPopupWindow = () => Window({
name: 'popup-window',
anchor: 'top',
child: PopupList(),
});
ags.Utils.timeout(1000, () => ags.Utils.execAsync([
'notify-send',
'Notification Center example',
'To have the panel popup run "ags toggle-window notification-center"' +
'\nPress ESC to close it.',
]).catch(console.error));
export default {
style: ags.App.configDir + '/style.css',
windows: [
NotificationsPopupWindow(),
NotificationCenter(),
]
}
+121
View File
@@ -0,0 +1,121 @@
const { Notifications } = ags.Service;
const { lookUpIcon, timeout } = ags.Utils;
const { Box, Icon, Label, EventBox, Button } = ags.Widget;
const NotificationIcon = ({ appEntry, appIcon, image }) => {
if (image) {
return Box({
valign: 'start',
hexpand: false,
className: 'icon img',
style: `
background-image: url("${image}");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
min-width: 78px;
min-height: 78px;
`,
});
}
let icon = 'dialog-information-symbolic';
if (lookUpIcon(appIcon))
icon = appIcon;
if (lookUpIcon(appEntry))
icon = appEntry;
return Box({
valign: 'start',
hexpand: false,
className: 'icon',
style: `
min-width: 78px;
min-height: 78px;
`,
children: [Icon({
icon, size: 58,
halign: 'center', hexpand: true,
valign: 'center', vexpand: true,
})],
});
};
export const Notification = ({ id, summary, body, actions, urgency, ...icon }) => EventBox({
className: `notification ${urgency}`,
onPrimaryClick: () => Notifications.dismiss(id),
properties: [['hovered', false]],
onHover: w => {
if (w._hovered)
return;
// if there are action buttons and they are hovered
// EventBox onHoverLost will fire off immediately,
// so to prevent this we delay it
timeout(300, () => w._hovered = true);
},
onHoverLost: w => {
if (!w._hovered)
return;
w._hovered = false;
Notifications.dismiss(id);
},
vexpand: false,
child: Box({
vertical: true,
children: [
Box({
children: [
NotificationIcon(icon),
Box({
hexpand: true,
vertical: true,
children: [
Box({
children: [
Label({
className: 'title',
xalign: 0,
justification: 'left',
hexpand: true,
maxWidthChars: 24,
truncate: 'end',
wrap: true,
label: summary,
useMarkup: summary.startsWith('<'),
}),
Button({
className: 'close-button',
valign: 'start',
child: Icon('window-close-symbolic'),
onClicked: () => Notifications.close(id),
}),
],
}),
Label({
className: 'description',
hexpand: true,
useMarkup: true,
xalign: 0,
justification: 'left',
label: body,
wrap: true,
}),
],
}),
],
}),
Box({
className: 'actions',
children: actions.map(action => Button({
className: 'action-button',
onClicked: () => Notifications.invoke(id, action.id),
hexpand: true,
child: Label(action.label),
})),
}),
],
}),
});
+97
View File
@@ -0,0 +1,97 @@
/* Assuming Adwaita-dark as Gtk theme */
#popup-window {
background-color: transparent;
}
#notification-center {
background-color: #232323;
}
#popup-window .notification > * {
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
}
#popup-window *,
#notification-center *{
outline: none;
color: white;
}
button {
border: none;
box-shadow: none;
border-radius: 7px;
background-color: rgba(255,255,255, 0.1);
}
button:hover {
background-color: rgba(255,255,255, 0.2);
}
button:active {
background-color: rgba(255,255,255, 0.3);
}
.notification > * {
margin: 8px;
padding: 8px;
border-radius: 15px;
background-color: #141414;
}
.notification .icon {
border-radius: 7px;
margin-right: 8px;
}
.notification .close-button {
padding: 0;
margin: 0;
min-height: 1.4em;
min-width: 1.4em;
border-radius: 1em;
}
.notification .title {
font-size: 1.1em;
color: white;
}
.notification .description {
color: rgba(255, 255, 255, 0.7);
}
.list {
margin: 8px;
min-width: 380px;
}
.action-button {
margin: 0 4px;
}
.action-button:first-child {
margin-left: 0;
}
.action-button:last-child {
margin-right: 0;
}
.action-button:last-child:first-child {
margin: 0;
}
.header {
padding: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.placeholder image {
font-size: 8em;
}
.placeholder label {
font-size: 1.4em;
}
+85
View File
@@ -0,0 +1,85 @@
import { Notification } from './notification.js';
const { Gtk } = imports.gi;
const { Notifications } = ags.Service;
const { Scrollable, Box, Icon, Label, Widget, Button, Stack } = ags.Widget;
const List = () => Box({
vertical: true,
vexpand: true,
connections: [[Notifications, box => {
box.children = Array.from(Notifications.notifications.values())
.map(n => Notification(n));
box.visible = Notifications.notifications.size > 0;
}]],
});
const Placeholder = () => Box({
className: 'placeholder',
vertical: true,
vexpand: true,
valign: 'center',
children: [
Icon('notifications-disabled-symbolic'),
Label('Your inbox is empty'),
],
connections: [
[Notifications, box => {
box.visible = Notifications.notifications.size === 0;
}],
],
});
export const NotificationList = () => Scrollable({
hscroll: 'never',
vscroll: 'automatic',
child: Box({
className: 'list',
vertical: true,
children: [
List(),
Placeholder(),
],
}),
});
export const ClearButton = () => Button({
onClicked: Notifications.clear,
connections: [[Notifications, button => {
button.sensitive = Notifications.notifications.size > 0;
}]],
child: Box({
children: [
Label('Clear'),
Stack({
items: [
['true', Icon('user-trash-full-symbolic')],
['false', Icon('user-trash-symbolic')],
],
connections: [[Notifications, stack => {
stack.shown = `${Notifications.notifications.size > 0}`;
}]],
}),
],
}),
});
export const DNDSwitch = () => Widget({
type: Gtk.Switch,
valign: 'center',
connections: [
['notify::active', ({ active }) => {
Notifications.dnd = active;
}],
],
});
export const PopupList = () => Box({
className: 'list',
style: 'padding: 1px;', // so it shows up
vertical: true,
connections: [[Notifications, box => {
box.children = Array.from(Notifications.popups.values())
.map(n => Notification(n));
}]],
});