NotificationDaemon: add support for resident notifications
Resident notifications don't get removed when they are clicked or one of their actions is invoked, and are only removed when the app that created them requests them to be removed or sends another notification. Remove the source when a notification associated with it is removed. Except if the source is a tray icon. Make sure that we pop down the tray when a notification is clicked or one of the actions of a non-resident notification is selected. Based on the initial patch by Jonathan Matthew. https://bugzilla.gnome.org/show_bug.cgi?id=633412
This commit is contained in:
parent
7279cc1db8
commit
2a19d5f143
@ -226,6 +226,7 @@ Notification.prototype = {
|
|||||||
_init: function(source, title, banner, params) {
|
_init: function(source, title, banner, params) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.urgent = false;
|
this.urgent = false;
|
||||||
|
this.resident = false;
|
||||||
this.expanded = false;
|
this.expanded = false;
|
||||||
this._useActionIcons = false;
|
this._useActionIcons = false;
|
||||||
this._customContent = false;
|
this._customContent = false;
|
||||||
@ -257,7 +258,7 @@ Notification.prototype = {
|
|||||||
function (actor, event) {
|
function (actor, event) {
|
||||||
if (!this._actionArea ||
|
if (!this._actionArea ||
|
||||||
!this._actionArea.contains(event.get_source()))
|
!this._actionArea.contains(event.get_source()))
|
||||||
this.emit('clicked');
|
this._onClicked();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// The first line should have the title, followed by the
|
// The first line should have the title, followed by the
|
||||||
@ -484,7 +485,7 @@ Notification.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._buttonBox.add(button);
|
this._buttonBox.add(button);
|
||||||
button.connect('clicked', Lang.bind(this, function() { this.emit('action-invoked', id); }));
|
button.connect('clicked', Lang.bind(this, this._onActionInvoked, id));
|
||||||
this._updated();
|
this._updated();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -492,6 +493,10 @@ Notification.prototype = {
|
|||||||
this.urgent = urgent;
|
this.urgent = urgent;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setResident: function(resident) {
|
||||||
|
this.resident = resident;
|
||||||
|
},
|
||||||
|
|
||||||
setUseActionIcons: function(useIcons) {
|
setUseActionIcons: function(useIcons) {
|
||||||
this._useActionIcons = useIcons;
|
this._useActionIcons = useIcons;
|
||||||
},
|
},
|
||||||
@ -696,6 +701,28 @@ Notification.prototype = {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onActionInvoked: function(actor, id) {
|
||||||
|
this.emit('action-invoked', id);
|
||||||
|
if (!this.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.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClicked: function() {
|
||||||
|
this.emit('clicked');
|
||||||
|
// 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');
|
||||||
|
if (!this.resident)
|
||||||
|
this.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
_onKeyPress: function(actor, event) {
|
_onKeyPress: function(actor, event) {
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.Escape) {
|
if (symbol == Clutter.Escape) {
|
||||||
@ -811,6 +838,7 @@ Source.prototype = {
|
|||||||
this.notification = null;
|
this.notification = null;
|
||||||
this._notificationDestroyedId = 0;
|
this._notificationDestroyedId = 0;
|
||||||
this._notificationClickedId = 0;
|
this._notificationClickedId = 0;
|
||||||
|
this._notificationRemoved();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -830,8 +858,13 @@ Source.prototype = {
|
|||||||
this._iconBin.child = icon;
|
this._iconBin.child = icon;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Default implementation is to do nothing, but subclass can override
|
// Default implementation is to do nothing, but subclasses can override
|
||||||
_notificationClicked: function(notification) {
|
_notificationClicked: function(notification) {
|
||||||
|
},
|
||||||
|
|
||||||
|
// Default implementation is to destroy this source, but subclasses can override
|
||||||
|
_notificationRemoved: function() {
|
||||||
|
this.destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Signals.addSignalMethods(Source.prototype);
|
Signals.addSignalMethods(Source.prototype);
|
||||||
@ -905,6 +938,7 @@ MessageTray.prototype = {
|
|||||||
this._notificationBin.hide();
|
this._notificationBin.hide();
|
||||||
this._notificationQueue = [];
|
this._notificationQueue = [];
|
||||||
this._notification = null;
|
this._notification = null;
|
||||||
|
this._notificationClickedId = 0;
|
||||||
|
|
||||||
this._summaryBin = new St.Bin({ anchor_gravity: Clutter.Gravity.NORTH_EAST });
|
this._summaryBin = new St.Bin({ anchor_gravity: Clutter.Gravity.NORTH_EAST });
|
||||||
this.actor.add_actor(this._summaryBin);
|
this.actor.add_actor(this._summaryBin);
|
||||||
@ -926,6 +960,7 @@ MessageTray.prototype = {
|
|||||||
this._summaryNotificationBoxPointer.actor.hide();
|
this._summaryNotificationBoxPointer.actor.hide();
|
||||||
|
|
||||||
this._summaryNotification = null;
|
this._summaryNotification = null;
|
||||||
|
this._summaryNotificationClickedId = 0;
|
||||||
this._clickedSummaryItem = null;
|
this._clickedSummaryItem = null;
|
||||||
this._clickedSummaryItemAllocationChangedId = 0;
|
this._clickedSummaryItemAllocationChangedId = 0;
|
||||||
this._expandedSummaryItem = null;
|
this._expandedSummaryItem = null;
|
||||||
@ -1465,6 +1500,8 @@ MessageTray.prototype = {
|
|||||||
|
|
||||||
_showNotification: function() {
|
_showNotification: function() {
|
||||||
this._notification = this._notificationQueue.shift();
|
this._notification = this._notificationQueue.shift();
|
||||||
|
this._notificationClickedId = this._notification.connect('done-displaying',
|
||||||
|
Lang.bind(this, this.escapeTray));
|
||||||
this._notificationBin.child = this._notification.actor;
|
this._notificationBin.child = this._notification.actor;
|
||||||
|
|
||||||
this._notificationBin.opacity = 0;
|
this._notificationBin.opacity = 0;
|
||||||
@ -1562,6 +1599,8 @@ MessageTray.prototype = {
|
|||||||
this._notificationBin.hide();
|
this._notificationBin.hide();
|
||||||
this._notificationBin.child = null;
|
this._notificationBin.child = null;
|
||||||
this._notification.collapseCompleted();
|
this._notification.collapseCompleted();
|
||||||
|
this._notification.disconnect(this._notificationClickedId);
|
||||||
|
this._notificationClickedId = 0;
|
||||||
this._notification = null;
|
this._notification = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1636,7 +1675,8 @@ MessageTray.prototype = {
|
|||||||
|
|
||||||
_showSummaryNotification: function() {
|
_showSummaryNotification: function() {
|
||||||
this._summaryNotification = this._clickedSummaryItem.source.notification;
|
this._summaryNotification = this._clickedSummaryItem.source.notification;
|
||||||
|
this._summaryNotificationClickedId = this._summaryNotification.connect('done-displaying',
|
||||||
|
Lang.bind(this, this.escapeTray));
|
||||||
let index = this._notificationQueue.indexOf(this._summaryNotification);
|
let index = this._notificationQueue.indexOf(this._summaryNotification);
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
this._notificationQueue.splice(index, 1);
|
this._notificationQueue.splice(index, 1);
|
||||||
@ -1706,6 +1746,8 @@ MessageTray.prototype = {
|
|||||||
this._summaryNotificationState = State.HIDDEN;
|
this._summaryNotificationState = State.HIDDEN;
|
||||||
this._summaryNotificationBoxPointer.bin.child = null;
|
this._summaryNotificationBoxPointer.bin.child = null;
|
||||||
this._summaryNotification.collapseCompleted();
|
this._summaryNotification.collapseCompleted();
|
||||||
|
this._summaryNotification.disconnect(this._summaryNotificationClickedId);
|
||||||
|
this._summaryNotificationClickedId = 0;
|
||||||
let summaryNotification = this._summaryNotification;
|
let summaryNotification = this._summaryNotification;
|
||||||
this._summaryNotification = null;
|
this._summaryNotification = null;
|
||||||
if (this._reNotifyWithSummaryNotificationAfterHide) {
|
if (this._reNotifyWithSummaryNotificationAfterHide) {
|
||||||
|
@ -291,7 +291,10 @@ NotificationDaemon.prototype = {
|
|||||||
function(n) {
|
function(n) {
|
||||||
delete this._notifications[id];
|
delete this._notifications[id];
|
||||||
}));
|
}));
|
||||||
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
|
notification.connect('action-invoked', Lang.bind(this,
|
||||||
|
function(n, actionId) {
|
||||||
|
this._emitActionInvoked(id, actionId);
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
notification.update(summary, body, { icon: iconActor,
|
notification.update(summary, body, { icon: iconActor,
|
||||||
bannerMarkup: true,
|
bannerMarkup: true,
|
||||||
@ -305,6 +308,7 @@ NotificationDaemon.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
||||||
|
notification.setResident(hints.resident == true);
|
||||||
|
|
||||||
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
||||||
source.notify(notification, sourceIconActor);
|
source.notify(notification, sourceIconActor);
|
||||||
@ -352,17 +356,13 @@ NotificationDaemon.prototype = {
|
|||||||
for (let id in this._sources) {
|
for (let id in this._sources) {
|
||||||
let source = this._sources[id];
|
let source = this._sources[id];
|
||||||
if (source.app == tracker.focus_app) {
|
if (source.app == tracker.focus_app) {
|
||||||
source.activated();
|
if (source.notification && !source.notification.resident)
|
||||||
|
source.notification.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_actionInvoked: function(notification, action, source, id) {
|
|
||||||
source.activated();
|
|
||||||
this._emitActionInvoked(id, action);
|
|
||||||
},
|
|
||||||
|
|
||||||
_emitNotificationClosed: function(id, reason) {
|
_emitNotificationClosed: function(id, reason) {
|
||||||
DBus.session.emit_signal('/org/freedesktop/Notifications',
|
DBus.session.emit_signal('/org/freedesktop/Notifications',
|
||||||
'org.freedesktop.Notifications',
|
'org.freedesktop.Notifications',
|
||||||
@ -421,6 +421,9 @@ Source.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_setApp: function() {
|
_setApp: function() {
|
||||||
|
if (this.app)
|
||||||
|
return;
|
||||||
|
|
||||||
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
|
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
|
||||||
if (!this.app)
|
if (!this.app)
|
||||||
return;
|
return;
|
||||||
@ -440,12 +443,10 @@ Source.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_notificationClicked: function(notification) {
|
_notificationClicked: function(notification) {
|
||||||
notification.destroy();
|
|
||||||
this.openApp();
|
this.openApp();
|
||||||
this.activated();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
activated: function() {
|
_notificationRemoved: function() {
|
||||||
if (!this._isTrayIcon)
|
if (!this._isTrayIcon)
|
||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user