mirror of
https://github.com/zoriya/ags.git
synced 2026-06-01 18:25:40 +00:00
add notification center example
This commit is contained in:
@@ -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(),
|
||||
]
|
||||
}
|
||||
@@ -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),
|
||||
})),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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));
|
||||
}]],
|
||||
});
|
||||
Reference in New Issue
Block a user