From dfd887066f92dece3e486d093c435229c03181bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 11 Feb 2015 20:41:56 +0100 Subject: [PATCH] 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 --- data/gnome-shell-theme.gresource.xml | 1 + data/theme/no-notifications.svg | 114 +++++++++++++++ js/ui/calendar.js | 198 ++++++++++++++++++++++++++- js/ui/main.js | 2 +- 4 files changed, 310 insertions(+), 5 deletions(-) create mode 100644 data/theme/no-notifications.svg diff --git a/data/gnome-shell-theme.gresource.xml b/data/gnome-shell-theme.gresource.xml index 9e270d056..f14532531 100644 --- a/data/gnome-shell-theme.gresource.xml +++ b/data/gnome-shell-theme.gresource.xml @@ -20,6 +20,7 @@ logged-in-indicator.svg more-results.svg no-events.svg + no-notifications.svg noise-texture.png page-indicator-active.svg page-indicator-inactive.svg diff --git a/data/theme/no-notifications.svg b/data/theme/no-notifications.svg new file mode 100644 index 000000000..23af5565e --- /dev/null +++ b/data/theme/no-notifications.svg @@ -0,0 +1,114 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/ui/calendar.js b/js/ui/calendar.js index 8204315d7..ecb609bf0 100644 --- a/js/ui/calendar.js +++ b/js/ui/calendar.js @@ -15,6 +15,7 @@ const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; const Main = imports.ui.main; +const MessageTray = imports.ui.messageTray; const Tweener = imports.ui.tweener; const Util = imports.misc.util; @@ -974,6 +975,61 @@ const Message = new Lang.Class({ }); 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({ 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({ Name: 'Placeholder', @@ -1284,14 +1443,41 @@ const Placeholder = new Lang.Class({ this._date = new Date(); - let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-events.svg'); - let gicon = new Gio.FileIcon({ file: file }); + let todayFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-notifications.svg'); + 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._label = new St.Label({ text: _("No Events") }); + this._label = new St.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._sections = new Map(); + this._notificationSection = new NotificationSection(); + this._addSection(this._notificationSection); + this._eventsSection = new EventsSection(); this._addSection(this._eventsSection); }, @@ -1377,5 +1566,6 @@ const MessageList = new Lang.Class({ setDate: function(date) { for (let section of this._sections.keys()) section.setDate(date); + this._placeholder.setDate(date); } }); diff --git a/js/ui/main.js b/js/ui/main.js index 2fb674c87..eb6545864 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -159,8 +159,8 @@ function _initializeUI() { if (LoginManager.canLock()) screenShield = new ScreenShield.ScreenShield(); - panel = new Panel.Panel(); messageTray = new MessageTray.MessageTray(); + panel = new Panel.Panel(); keyboard = new Keyboard.Keyboard(); notificationDaemon = new NotificationDaemon.NotificationDaemon(); windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();