[MessageTray] Clean up source-vs-notification icon handling
A Source needs exactly one summary icon (which in the case of a trayicon-based source won't even be just an image), but possibly many notification icons, which may vary for successive notifications (particularly in the case of NotificationDaemon notifications). So differentiate these cases in the API. https://bugzilla.gnome.org/show_bug.cgi?id=627303
This commit is contained in:
parent
1951812a47
commit
6f57f07214
@ -19,7 +19,6 @@ const SUMMARY_TIMEOUT = 1;
|
|||||||
|
|
||||||
const HIDE_TIMEOUT = 0.2;
|
const HIDE_TIMEOUT = 0.2;
|
||||||
|
|
||||||
const ICON_SIZE = 24;
|
|
||||||
const BUTTON_ICON_SIZE = 36;
|
const BUTTON_ICON_SIZE = 36;
|
||||||
|
|
||||||
const MAX_SOURCE_TITLE_WIDTH = 180;
|
const MAX_SOURCE_TITLE_WIDTH = 180;
|
||||||
@ -46,10 +45,14 @@ function _cleanMarkup(text) {
|
|||||||
// @banner: the banner text
|
// @banner: the banner text
|
||||||
// @params: optional additional params
|
// @params: optional additional params
|
||||||
//
|
//
|
||||||
// Creates a notification. In banner mode, it will show
|
// Creates a notification. In banner mode, it will show an
|
||||||
// @source's icon, @title (in bold) and @banner, all on a single line
|
// icon, @title (in bold) and @banner, all on a single line
|
||||||
// (with @banner ellipsized if necessary).
|
// (with @banner ellipsized if necessary).
|
||||||
//
|
//
|
||||||
|
// By default, the icon shown is created by calling
|
||||||
|
// source.createNotificationIcon(). However, you can override this
|
||||||
|
// by passing an 'icon' parameter in @params.
|
||||||
|
//
|
||||||
// Additional notification details can be added, in which case the
|
// Additional notification details can be added, in which case the
|
||||||
// notification can be expanded by moving the pointer into it. In
|
// notification can be expanded by moving the pointer into it. In
|
||||||
// expanded mode, the banner text disappears, and there can be one or
|
// expanded mode, the banner text disappears, and there can be one or
|
||||||
@ -150,6 +153,7 @@ Notification.prototype = {
|
|||||||
update: function(title, banner, params) {
|
update: function(title, banner, params) {
|
||||||
params = Params.parse(params, { bannerBody: this._bannerBody,
|
params = Params.parse(params, { bannerBody: this._bannerBody,
|
||||||
body: null,
|
body: null,
|
||||||
|
icon: null,
|
||||||
clear: false });
|
clear: false });
|
||||||
|
|
||||||
if (this._icon)
|
if (this._icon)
|
||||||
@ -167,7 +171,7 @@ Notification.prototype = {
|
|||||||
|
|
||||||
this._bannerBody = params.bannerBody;
|
this._bannerBody = params.bannerBody;
|
||||||
|
|
||||||
this._icon = this.source.createIcon(ICON_SIZE);
|
this._icon = params.icon || this.source.createNotificationIcon();
|
||||||
this.actor.add(this._icon, { row: 0,
|
this.actor.add(this._icon, { row: 0,
|
||||||
col: 0,
|
col: 0,
|
||||||
x_expand: false,
|
x_expand: false,
|
||||||
@ -560,21 +564,30 @@ Notification.prototype = {
|
|||||||
};
|
};
|
||||||
Signals.addSignalMethods(Notification.prototype);
|
Signals.addSignalMethods(Notification.prototype);
|
||||||
|
|
||||||
function Source(title, createIcon) {
|
function Source(title) {
|
||||||
this._init(title, createIcon);
|
this._init(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
Source.prototype = {
|
Source.prototype = {
|
||||||
_init: function(title, createIcon) {
|
ICON_SIZE: 24,
|
||||||
|
|
||||||
|
_init: function(title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
if (createIcon)
|
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
|
||||||
this.createIcon = createIcon;
|
height: this.ICON_SIZE });
|
||||||
},
|
},
|
||||||
|
|
||||||
// This can be overridden by a subclass, or by the createIcon
|
// Called to create a new icon actor (of size this.ICON_SIZE).
|
||||||
// parameter to _init()
|
// Must be overridden by the subclass if you do not pass icons
|
||||||
createIcon: function(size) {
|
// explicitly to the Notification() constructor.
|
||||||
throw new Error('no implementation of createIcon in ' + this);
|
createNotificationIcon: function() {
|
||||||
|
throw new Error('no implementation of createNotificationIcon in ' + this);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Unlike createNotificationIcon, this always returns the same actor;
|
||||||
|
// there is only one summary icon actor for a Source.
|
||||||
|
getSummaryIcon: function() {
|
||||||
|
return this._iconBin;
|
||||||
},
|
},
|
||||||
|
|
||||||
notify: function(notification) {
|
notify: function(notification) {
|
||||||
@ -600,6 +613,15 @@ Source.prototype = {
|
|||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
this.emit('destroy');
|
this.emit('destroy');
|
||||||
|
},
|
||||||
|
|
||||||
|
//// Protected methods ////
|
||||||
|
|
||||||
|
// The subclass must call this at least once to set the summary icon.
|
||||||
|
_setSummaryIcon: function(icon) {
|
||||||
|
if (this._iconBin.child)
|
||||||
|
this._iconBin.child.destroy();
|
||||||
|
this._iconBin.child = icon;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Signals.addSignalMethods(Source.prototype);
|
Signals.addSignalMethods(Source.prototype);
|
||||||
@ -626,7 +648,7 @@ SummaryItem.prototype = {
|
|||||||
this._sourceBox.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
this._sourceBox.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
this._sourceBox.connect('allocate', Lang.bind(this, this._allocate));
|
this._sourceBox.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
|
|
||||||
this._sourceIcon = source.createIcon(ICON_SIZE);
|
this._sourceIcon = source.getSummaryIcon();
|
||||||
this._sourceTitleBin = new St.Bin({ y_align: St.Align.MIDDLE, x_fill: true });
|
this._sourceTitleBin = new St.Bin({ y_align: St.Align.MIDDLE, x_fill: true });
|
||||||
this._sourceTitle = new St.Label({ style_class: 'source-title',
|
this._sourceTitle = new St.Label({ style_class: 'source-title',
|
||||||
text: source.title });
|
text: source.title });
|
||||||
|
@ -141,6 +141,37 @@ NotificationDaemon.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_iconForNotificationData: function(icon, hints, size) {
|
||||||
|
let textureCache = St.TextureCache.get_default();
|
||||||
|
|
||||||
|
if (icon) {
|
||||||
|
if (icon.substr(0, 7) == 'file://')
|
||||||
|
return textureCache.load_uri_async(icon, size, size);
|
||||||
|
else if (icon[0] == '/') {
|
||||||
|
let uri = GLib.filename_to_uri(icon, null);
|
||||||
|
return textureCache.load_uri_async(uri, size, size);
|
||||||
|
} else
|
||||||
|
return textureCache.load_icon_name(icon, size);
|
||||||
|
} else if (hints.icon_data) {
|
||||||
|
let [width, height, rowStride, hasAlpha,
|
||||||
|
bitsPerSample, nChannels, data] = hints.icon_data;
|
||||||
|
return textureCache.load_from_raw(data, data.length, hasAlpha,
|
||||||
|
width, height, rowStride, size);
|
||||||
|
} else {
|
||||||
|
let stockIcon;
|
||||||
|
switch (hints.urgency) {
|
||||||
|
case Urgency.LOW:
|
||||||
|
case Urgency.NORMAL:
|
||||||
|
stockIcon = 'gtk-dialog-info';
|
||||||
|
break;
|
||||||
|
case Urgency.CRITICAL:
|
||||||
|
stockIcon = 'gtk-dialog-error';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return textureCache.load_icon_name(stockIcon, size);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
Notify: function(appName, replacesId, icon, summary, body,
|
Notify: function(appName, replacesId, icon, summary, body,
|
||||||
actions, hints, timeout) {
|
actions, hints, timeout) {
|
||||||
let source = this._sources[appName];
|
let source = this._sources[appName];
|
||||||
@ -164,7 +195,7 @@ NotificationDaemon.prototype = {
|
|||||||
// been acknowledged.
|
// been acknowledged.
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
let title = appNameMap[appName] || appName;
|
let title = appNameMap[appName] || appName;
|
||||||
source = new Source(title, icon, hints);
|
source = new Source(title);
|
||||||
Main.messageTray.add(source);
|
Main.messageTray.add(source);
|
||||||
this._sources[appName] = source;
|
this._sources[appName] = source;
|
||||||
|
|
||||||
@ -184,8 +215,6 @@ NotificationDaemon.prototype = {
|
|||||||
if (app)
|
if (app)
|
||||||
source.setApp(app);
|
source.setApp(app);
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
source.update(icon, hints);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
summary = GLib.markup_escape_text(summary, -1);
|
summary = GLib.markup_escape_text(summary, -1);
|
||||||
@ -205,10 +234,12 @@ NotificationDaemon.prototype = {
|
|||||||
notification = this._currentNotifications[id];
|
notification = this._currentNotifications[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
||||||
if (notification == null) {
|
if (notification == null) {
|
||||||
id = nextNotificationId++;
|
id = nextNotificationId++;
|
||||||
notification = new MessageTray.Notification(source, summary, body,
|
notification = new MessageTray.Notification(source, summary, body,
|
||||||
{ bannerBody: true });
|
{ bannerBody: true,
|
||||||
|
icon: iconActor });
|
||||||
this._currentNotifications[id] = notification;
|
this._currentNotifications[id] = notification;
|
||||||
notification.connect('dismissed', Lang.bind(this,
|
notification.connect('dismissed', Lang.bind(this,
|
||||||
function(n) {
|
function(n) {
|
||||||
@ -220,7 +251,8 @@ NotificationDaemon.prototype = {
|
|||||||
}));
|
}));
|
||||||
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
|
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
|
||||||
} else {
|
} else {
|
||||||
notification.update(summary, body, { clear: true });
|
notification.update(summary, body, { icon: iconActor,
|
||||||
|
clear: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actions.length) {
|
if (actions.length) {
|
||||||
@ -230,7 +262,8 @@ NotificationDaemon.prototype = {
|
|||||||
|
|
||||||
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
||||||
|
|
||||||
source.notify(notification);
|
let sourceIconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
||||||
|
source.notify(sourceIconActor, notification);
|
||||||
return id;
|
return id;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -300,57 +333,23 @@ NotificationDaemon.prototype = {
|
|||||||
|
|
||||||
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
|
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
|
||||||
|
|
||||||
function Source(title, icon, hints) {
|
function Source(title) {
|
||||||
this._init(title, icon, hints);
|
this._init(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
Source.prototype = {
|
Source.prototype = {
|
||||||
__proto__: MessageTray.Source.prototype,
|
__proto__: MessageTray.Source.prototype,
|
||||||
|
|
||||||
_init: function(title, icon, hints) {
|
_init: function(title) {
|
||||||
MessageTray.Source.prototype._init.call(this, title);
|
MessageTray.Source.prototype._init.call(this, title);
|
||||||
|
|
||||||
this.app = null;
|
this.app = null;
|
||||||
this._openAppRequested = false;
|
this._openAppRequested = false;
|
||||||
|
|
||||||
this.update(icon, hints);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
update: function(icon, hints) {
|
notify: function(icon, notification) {
|
||||||
this._icon = icon;
|
this._setSummaryIcon(icon);
|
||||||
this._iconData = hints.icon_data;
|
MessageTray.Source.prototype.notify.call(this, notification);
|
||||||
this._urgency = hints.urgency;
|
|
||||||
},
|
|
||||||
|
|
||||||
createIcon: function(size) {
|
|
||||||
let textureCache = St.TextureCache.get_default();
|
|
||||||
|
|
||||||
if (this._icon) {
|
|
||||||
if (this._icon.substr(0, 7) == 'file://')
|
|
||||||
return textureCache.load_uri_async(this._icon, size, size);
|
|
||||||
else if (this._icon[0] == '/') {
|
|
||||||
let uri = GLib.filename_to_uri(this._icon, null);
|
|
||||||
return textureCache.load_uri_async(uri, size, size);
|
|
||||||
} else
|
|
||||||
return textureCache.load_icon_name(this._icon, size);
|
|
||||||
} else if (this._iconData) {
|
|
||||||
let [width, height, rowStride, hasAlpha,
|
|
||||||
bitsPerSample, nChannels, data] = this._iconData;
|
|
||||||
return textureCache.load_from_raw(data, data.length, hasAlpha,
|
|
||||||
width, height, rowStride, size);
|
|
||||||
} else {
|
|
||||||
let stockIcon;
|
|
||||||
switch (this._urgency) {
|
|
||||||
case Urgency.LOW:
|
|
||||||
case Urgency.NORMAL:
|
|
||||||
stockIcon = 'gtk-dialog-info';
|
|
||||||
break;
|
|
||||||
case Urgency.CRITICAL:
|
|
||||||
stockIcon = 'gtk-dialog-error';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return textureCache.load_icon_name(stockIcon, size);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
clicked: function() {
|
clicked: function() {
|
||||||
|
@ -475,10 +475,13 @@ Source.prototype = {
|
|||||||
this._receivedId = this._channelText.connect('Received', Lang.bind(this, this._messageReceived));
|
this._receivedId = this._channelText.connect('Received', Lang.bind(this, this._messageReceived));
|
||||||
|
|
||||||
this._channelText.ListPendingMessagesRemote(false, Lang.bind(this, this._gotPendingMessages));
|
this._channelText.ListPendingMessagesRemote(false, Lang.bind(this, this._gotPendingMessages));
|
||||||
|
|
||||||
|
this._setSummaryIcon(this.createNotificationIcon());
|
||||||
},
|
},
|
||||||
|
|
||||||
createIcon: function(size) {
|
createNotificationIcon: function() {
|
||||||
return contactManager.createAvatar(this._conn, this._targetHandle, size);
|
return contactManager.createAvatar(this._conn, this._targetHandle,
|
||||||
|
this.ICON_SIZE);
|
||||||
},
|
},
|
||||||
|
|
||||||
clicked: function() {
|
clicked: function() {
|
||||||
|
@ -94,10 +94,11 @@ Source.prototype = {
|
|||||||
MessageTray.Source.prototype._init.call(this, app.get_name());
|
MessageTray.Source.prototype._init.call(this, app.get_name());
|
||||||
this._window = window;
|
this._window = window;
|
||||||
this._app = app;
|
this._app = app;
|
||||||
|
this._setSummaryIcon(this.createNotificationIcon());
|
||||||
},
|
},
|
||||||
|
|
||||||
createIcon : function(size) {
|
createNotificationIcon : function() {
|
||||||
return this._app.create_icon_texture(size);
|
return this._app.create_icon_texture(this.ICON_SIZE);
|
||||||
},
|
},
|
||||||
|
|
||||||
clicked : function() {
|
clicked : function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user