calendar: Add NotificationSection to message list

Display notifications that have not been dismissed in the message
list - eventually this will replace the existing message tray summary.
Notification messages show icon, title and one line of the body and
can be clicked to activate the default action. However they cannot be
expanded, so other actions or the full body text are not accessible
in this mode.

https://bugzilla.gnome.org/show_bug.cgi?id=744817
This commit is contained in:
Florian Müllner 2015-02-11 20:41:56 +01:00
parent 025985eb07
commit dfd887066f
4 changed files with 310 additions and 5 deletions

View File

@ -20,6 +20,7 @@
<file>logged-in-indicator.svg</file> <file>logged-in-indicator.svg</file>
<file>more-results.svg</file> <file>more-results.svg</file>
<file>no-events.svg</file> <file>no-events.svg</file>
<file>no-notifications.svg</file>
<file>noise-texture.png</file> <file>noise-texture.png</file>
<file>page-indicator-active.svg</file> <file>page-indicator-active.svg</file>
<file>page-indicator-inactive.svg</file> <file>page-indicator-inactive.svg</file>

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg3393"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="New document 2">
<defs
id="defs3395" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="32"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="697"
inkscape:window-height="613"
inkscape:window-x="100"
inkscape:window-y="77"
inkscape:window-maximized="0" />
<metadata
id="metadata3398">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
style="display:inline"
transform="matrix(4,0,0,4,0.29733827,-0.35415646)"
id="g19245">
<g
id="g19247"
inkscape:label="status"
style="display:inline"
transform="translate(-323.02908,-649.02581)" />
<g
id="g19249"
inkscape:label="devices"
transform="translate(-323.02908,-649.02581)" />
<g
id="g19251"
inkscape:label="apps"
transform="translate(-323.02908,-649.02581)">
<path
inkscape:connector-curvature="0"
d="m 331.9377,653 c 0.0187,0.16677 0.0625,0.32822 0.0625,0.5 0,2.48528 -2.01472,4.5 -4.5,4.5 -0.11769,0 -0.22834,-0.0224 -0.34375,-0.0312 l 0,2.21875 c 0,1.00412 0.80838,1.8125 1.8125,1.8125 l 1.54511,-5e-5 2,2.04688 2.0625,-2.04688 1.61114,0 c 1.00413,0 1.8125,-0.80838 1.8125,-1.8125 l 0,-5.375 c 0,-1.00412 -0.80837,-1.8125 -1.8125,-1.8125 z"
id="path19253"
sodipodi:nodetypes="csscsscccssssc"
style="opacity:0.5;color:#000000;fill:#c3c3c3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
inkscape:connector-curvature="0"
d="m 327.5002,650 c -1.933,0 -3.5,1.567 -3.5,3.5 0,1.933 1.567,3.5 3.5,3.5 1.933,0 3.5,-1.567 3.5,-3.5 0,-1.933 -1.567,-3.5 -3.5,-3.5 z m -0.53125,1 1.03125,0 -0.0625,1.375 a 0.19951718,0.19951718 0 0 0 0,0.0625 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0.125,0.125 0.19951718,0.19951718 0 0 0 0.0312,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 l 1.15625,-0.75 0.5,0.90625 -1.21875,0.625 a 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 -0.0312,0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0.0937 0.19951718,0.19951718 0 0 0 0,0.0625 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0.0312,0.0625 0.19951718,0.19951718 0 0 0 0.0312,0.0312 0.19951718,0.19951718 0 0 0 0.0312,0.0312 l 1.25,0.625 -0.53125,0.90625 -1.15625,-0.781 a 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0625,0 0.19951718,0.19951718 0 0 0 -0.125,0.0937 0.19951718,0.19951718 0 0 0 -0.0312,0.0312 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0,0.0625 l 0.0625,1.3751 -1.03125,0 0.0937,-1.375 a 0.19951718,0.19951718 0 0 0 -0.0312,-0.0937 0.19951718,0.19951718 0 0 0 -0.0312,-0.0625 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 -0.0937,0.0312 l -1.1875,0.78125 -0.5,-0.90625 1.25,-0.625 a 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 0,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,-0.0625 0.19951718,0.19951718 0 0 0 -0.0312,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0 l -1.25,-0.625 0.5,-0.90625 1.1875,0.75 a 0.19951718,0.19951718 0 0 0 0.0312,0.0312 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0312,0 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 L 326.96895,651 z"
id="path19255"
style="color:#000000;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
id="g19257"
inkscape:label="places"
transform="translate(-323.02908,-649.02581)" />
<g
id="g19259"
inkscape:label="mimetypes"
transform="translate(-323.02908,-649.02581)" />
<g
id="g19261"
inkscape:label="emblems"
style="display:inline"
transform="translate(-323.02908,-649.02581)" />
<g
id="g19263"
inkscape:label="emotes"
style="display:inline"
transform="translate(-323.02908,-649.02581)" />
<g
id="g19265"
inkscape:label="categories"
style="display:inline"
transform="translate(-323.02908,-649.02581)" />
<g
id="g19267"
inkscape:label="actions"
style="display:inline"
transform="translate(-323.02908,-649.02581)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -15,6 +15,7 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const Util = imports.misc.util; const Util = imports.misc.util;
@ -974,6 +975,61 @@ const Message = new Lang.Class({
}); });
Signals.addSignalMethods(Message.prototype); Signals.addSignalMethods(Message.prototype);
const NotificationMessage = new Lang.Class({
Name: 'NotificationMessage',
Extends: Message,
_init: function(notification) {
this.notification = notification;
this.setUseBodyMarkup(notification.bannerBodyMarkup);
this.parent(notification.title, notification.bannerBodyText);
this.setIcon(this._getIcon());
this.connect('close', Lang.bind(this,
function() {
this._closed = true;
this.notification.destroy(MessageTray.NotificationDestroyedReason.DISMISSED);
}));
notification.connect('destroy', Lang.bind(this,
function() {
if (!this._closed)
this.emit('close');
}));
this._updatedId = notification.connect('updated',
Lang.bind(this, this._onUpdated));
},
_getIcon: function() {
if (this.notification.gicon)
return new St.Icon({ gicon: this.notification.gicon, icon_size: 48 });
else
return this.notification.source.createIcon(48);
},
_onUpdated: function(n, clear) {
this.setIcon(this._getIcon());
this.setTitle(n.title);
this.setBody(n.bannerBodyText);
this.setUseBodyMarkup(n.bannerBodyMarkup);
},
canClear: function() {
return !this.notification.resident;
},
_onClicked: function() {
this.notification.activate();
},
_onDestroy: function() {
if (this._updatedId)
this.notification.disconnect(this._updatedId);
this._updatedId = 0;
}
});
const MessageListSection = new Lang.Class({ const MessageListSection = new Lang.Class({
Name: 'MessageListSection', Name: 'MessageListSection',
@ -1275,6 +1331,109 @@ const EventsSection = new Lang.Class({
} }
}); });
const NotificationSection = new Lang.Class({
Name: 'NotificationSection',
Extends: MessageListSection,
_init: function() {
this.parent('Notifications');
this._sources = new Map();
this._nUrgent = 0;
Main.messageTray.connect('source-added', Lang.bind(this, this._sourceAdded));
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
this._sourceAdded(Main.messageTray, source);
}));
this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_sourceAdded: function(tray, source) {
let obj = {
destroyId: 0,
notificationAddedId: 0,
};
obj.destroyId = source.connect('destroy', Lang.bind(this, function(source) {
this._onSourceDestroy(source, obj);
}));
obj.notificationAddedId = source.connect('notification-added',
Lang.bind(this, this._onNotificationAdded));
this._sources.set(source, obj);
},
_onNotificationAdded: function(source, notification) {
let message = new NotificationMessage(notification);
let time = new Date().toLocaleFormat(C_("event list time", "%H\u2236%M"));
message.setSecondaryActor(new St.Label({ style_class: 'event-time',
x_align: Clutter.ActorAlign.END,
text: time }));
let isUrgent = notification.urgency == MessageTray.Urgency.CRITICAL;
if (isUrgent) {
// Keep track of urgent notifications to keep them on top
this._nUrgent++;
let id = notification.connect('destroy', Lang.bind(this,
function() {
notification.disconnect(id);
this._nUrgent--;
}));
} else if (this.mapped) {
// Only acknowledge non-urgent notifications in case it
// has important actions that are inaccessible when not
// shown as banner
notification.acknowledged = true;
}
let index = isUrgent ? 0 : this._nUrgent;
this.addMessageAtIndex(message, index, this.actor.mapped);
},
_onSourceDestroy: function(source, obj) {
source.disconnect(obj.destroyId);
source.disconnect(obj.notificationAddedId);
this._sources.delete(source);
},
_onMapped: function() {
if (!this.actor.mapped)
return;
for (let message of this._messages.keys())
if (message.notification.urgency != MessageTray.Urgency.CRITICAL)
message.notification.acknowledged = true;
},
_onTitleClicked: function() {
this.parent();
let app = Shell.AppSystem.get_default().lookup_app('gnome-notifications-panel.desktop');
if (!app) {
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
return;
}
app.activate();
},
_syncVisible: function() {
this.actor.visible = !this.empty && this._isToday();
},
_sessionUpdated: function() {
this._title.reactive = Main.sessionMode.allowSettings;
}
});
const Placeholder = new Lang.Class({ const Placeholder = new Lang.Class({
Name: 'Placeholder', Name: 'Placeholder',
@ -1284,14 +1443,41 @@ const Placeholder = new Lang.Class({
this._date = new Date(); this._date = new Date();
let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-events.svg'); let todayFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-notifications.svg');
let gicon = new Gio.FileIcon({ file: file }); let otherFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-events.svg');
this._todayIcon = new Gio.FileIcon({ file: todayFile });
this._otherIcon = new Gio.FileIcon({ file: otherFile });
this._icon = new St.Icon({ gicon: gicon }); this._icon = new St.Icon();
this.actor.add_actor(this._icon); this.actor.add_actor(this._icon);
this._label = new St.Label({ text: _("No Events") }); this._label = new St.Label();
this.actor.add_actor(this._label); this.actor.add_actor(this._label);
this._sync();
},
setDate: function(date) {
if (_sameDay(this._date, date))
return;
this._date = date;
this._sync();
},
_sync: function() {
let isToday = _sameDay(this._date, new Date());
if (isToday && this._icon.gicon == this._todayIcon)
return;
if (!isToday && this._icon.gicon == this._otherIcon)
return;
if (isToday) {
this._icon.gicon = this._todayIcon;
this._label.text = _("No Notifications");
} else {
this._icon.gicon = this._otherIcon;
this._label.text = _("No Events");
}
} }
}); });
@ -1320,6 +1506,9 @@ const MessageList = new Lang.Class({
this._scrollView.add_actor(this._sectionList); this._scrollView.add_actor(this._sectionList);
this._sections = new Map(); this._sections = new Map();
this._notificationSection = new NotificationSection();
this._addSection(this._notificationSection);
this._eventsSection = new EventsSection(); this._eventsSection = new EventsSection();
this._addSection(this._eventsSection); this._addSection(this._eventsSection);
}, },
@ -1377,5 +1566,6 @@ const MessageList = new Lang.Class({
setDate: function(date) { setDate: function(date) {
for (let section of this._sections.keys()) for (let section of this._sections.keys())
section.setDate(date); section.setDate(date);
this._placeholder.setDate(date);
} }
}); });

View File

@ -159,8 +159,8 @@ function _initializeUI() {
if (LoginManager.canLock()) if (LoginManager.canLock())
screenShield = new ScreenShield.ScreenShield(); screenShield = new ScreenShield.ScreenShield();
panel = new Panel.Panel();
messageTray = new MessageTray.MessageTray(); messageTray = new MessageTray.MessageTray();
panel = new Panel.Panel();
keyboard = new Keyboard.Keyboard(); keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon(); notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();