diff --git a/data/theme/gnome-shell-sass b/data/theme/gnome-shell-sass index 860102c31..de2baea3f 160000 --- a/data/theme/gnome-shell-sass +++ b/data/theme/gnome-shell-sass @@ -1 +1 @@ -Subproject commit 860102c31bda598df340f2d68ea5be18e52da176 +Subproject commit de2baea3f921a28160a9be154683776338c1b5c2 diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index f9e2086b7..63c79d940 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -1146,6 +1146,39 @@ StScrollBar { .url-highlighter { link-color: #215d9c; } +.notification-banner { + font-size: 11pt; + width: 34em; + margin: 5px; + border-radius: 6px; + color: #eeeeec; + background-color: #2e3436; + border: 1px solid #1c1f1f; } + .notification-banner:hover { + background-color: #2e3436; } + .notification-banner:focus { + background-color: #2e3436; } + .notification-banner .notification-icon { + padding: 5px; } + .notification-banner .notification-content { + padding: 5px; + spacing: 5px; } + .notification-banner .secondary-icon { + icon-size: 1.09em; } + .notification-banner .notification-actions { + background-color: #1c1f1f; + padding-top: 2px; + spacing: 1px; } + .notification-banner .notification-button { + padding: 4px 4px 5px; + background-color: #222728; } + .notification-banner .notification-button:first-child { + border-radius: 0 0 0 6px; } + .notification-banner .notification-button:last-child { + border-radius: 0 0 6px 0; } + .notification-banner .notification-button:hover, .notification-banner .notification-buttonfocus { + background-color: #292f30; } + .notification { font-size: 11pt; width: 34em; diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js index a9ee24f06..32f8578dd 100644 --- a/js/ui/messageTray.js +++ b/js/ui/messageTray.js @@ -13,6 +13,7 @@ const Shell = imports.gi.Shell; const Signals = imports.signals; const St = imports.gi.St; +const Calendar = imports.ui.calendar; const GnomeSession = imports.misc.gnomeSession; const Main = imports.ui.main; const Params = imports.misc.params; @@ -1105,6 +1106,97 @@ const Notification = new Lang.Class({ }); Signals.addSignalMethods(Notification.prototype); +const NotificationBanner = new Lang.Class({ + Name: 'NotificationBanner', + Extends: Calendar.NotificationMessage, + + _init: function(notification) { + this.parent(notification); + + this.actor.add_style_class_name('notification-banner'); + this.actor.connect('destroy', Lang.bind(this, this._onDestroyed)); + + this._buttonBox = null; + + this._addActions(); + this._addSecondaryIcon(); + + this._activatedId = this.notification.connect('activated', + Lang.bind(this, function() { + // We hide all types of notifications once the user clicks on + // them because the common outcome of clicking should be the + // relevant window being brought forward and the user's + // attention switching to the window. + this.emit('done-displaying'); + })); + }, + + _onDestroyed: function() { + this.notification.disconnect(this._activatedId); + }, + + _onUpdated: function(n, clear) { + this.parent(n, clear); + + if (clear) { + this.setSecondaryActor(null); + this.setActionArea(null); + this._buttonBox = null; + } + + this._addActions(); + this._addSecondaryIcon(); + }, + + _addActions: function() { + this.notification.actions.forEach(Lang.bind(this, + function(action) { + this.addAction(action.label, action.callback); + })); + }, + + _addSecondaryIcon: function() { + if (this.notification.secondaryGIcon) { + let icon = new St.Icon({ gicon: this.notification.secondaryGIcon }); + this.setSecondaryActor(icon); + } + }, + + addButton: function(button, callback) { + if (!this._buttonBox) { + this._buttonBox = new St.BoxLayout({ style_class: 'notification-actions', + x_expand: true }); + this.setActionArea(this._buttonBox); + global.focus_manager.add_group(this._buttonBox); + } + + this._buttonBox.add(button); + button.connect('clicked', Lang.bind(this, function() { + callback(); + + if (!this.notification.resident) { + // We don't hide a resident notification when the user invokes one of its actions, + // because it is common for such notifications to update themselves with new + // information based on the action. We'd like to display the updated information + // in place, rather than pop-up a new notification. + this.emit('done-displaying'); + this.notification.destroy(); + } + })); + + return button; + }, + + addAction: function(label, callback) { + let button = new St.Button({ style_class: 'notification-button', + label: label, + x_expand: true, + can_focus: true }); + + return this.addButton(button, callback); + } +}); + const SourceActor = new Lang.Class({ Name: 'SourceActor',