messageTray: implement showing images in notifications

Images are part of the notification spec, so we should support them.

Marina Zhurakhinskaya provided some code for getting the layout right
for this patch.

https://bugzilla.gnome.org/show_bug.cgi?id=621009
This commit is contained in:
Neha Doijode 2011-08-29 22:41:24 +05:30 committed by Marina Zhurakhinskaya
parent d227ddfc88
commit aabe56ba79
3 changed files with 94 additions and 14 deletions

View File

@ -1070,6 +1070,14 @@ StTooltip StLabel {
padding-bottom: 8px; padding-bottom: 8px;
} }
/* We use row-span = 2 for the image cell, which prevents its height preferences to be
taken into account during allocation, so its height ends up being limited by the height
of the content in the other rows. To avoid showing a stretched image, we set the minimum
height of the table to be ICON_SIZE + IMAGE_SIZE + spacing-rows = 24 + 125 + 10 = 159 */
.notification-with-image {
min-height: 159px;
}
.summary-boxpointer { .summary-boxpointer {
-arrow-border-radius: 8px; -arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9); -arrow-background-color: rgba(0,0,0,0.9);

View File

@ -396,6 +396,8 @@ function Notification(source, title, banner, params) {
} }
Notification.prototype = { Notification.prototype = {
IMAGE_SIZE: 125,
_init: function(source, title, banner, params) { _init: function(source, title, banner, params) {
this.source = source; this.source = source;
this.urgency = Urgency.NORMAL; this.urgency = Urgency.NORMAL;
@ -412,6 +414,7 @@ Notification.prototype = {
this._titleDirection = St.TextDirection.NONE; this._titleDirection = St.TextDirection.NONE;
this._spacing = 0; this._spacing = 0;
this._scrollPolicy = Gtk.PolicyType.AUTOMATIC; this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
this._imageBin = null;
source.connect('destroy', Lang.bind(this, source.connect('destroy', Lang.bind(this,
function (source, reason) { function (source, reason) {
@ -441,6 +444,16 @@ Notification.prototype = {
this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate)); this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate));
this._table.add(this._bannerBox, { row: 0, this._table.add(this._bannerBox, { row: 0,
col: 1, col: 1,
col_span: 2,
x_expand: false,
y_expand: false,
y_fill: false });
// This is an empty cell that overlaps with this._bannerBox cell to ensure
// that this._bannerBox cell expands horizontally, while not forcing the
// this._imageBin that is also in col: 2 to expand horizontally.
this._table.add(new St.Bin(), { row: 0,
col: 2,
y_expand: false, y_expand: false,
y_fill: false }); y_fill: false });
@ -495,7 +508,10 @@ Notification.prototype = {
this._actionArea = null; this._actionArea = null;
this._buttonBox = null; this._buttonBox = null;
} }
if (!this._scrollArea && !this._actionArea) if (this._imageBin && params.clear)
this.unsetImage();
if (!this._scrollArea && !this._actionArea && !this._imageBin)
this._table.remove_style_class_name('multi-line-notification'); this._table.remove_style_class_name('multi-line-notification');
this._icon = params.icon || this.source.createNotificationIcon(); this._icon = params.icon || this.source.createNotificationIcon();
@ -559,7 +575,9 @@ Notification.prototype = {
vscrollbar_policy: this._scrollPolicy, vscrollbar_policy: this._scrollPolicy,
hscrollbar_policy: Gtk.PolicyType.NEVER, hscrollbar_policy: Gtk.PolicyType.NEVER,
style_class: 'vfade' }); style_class: 'vfade' });
this._table.add(this._scrollArea, { row: 1, col: 1 }); this._table.add(this._scrollArea, { row: 1,
col: 2 });
this._updateLastColumnSettings();
this._contentArea = new St.BoxLayout({ name: 'notification-body', this._contentArea = new St.BoxLayout({ name: 'notification-body',
vertical: true }); vertical: true });
this._scrollArea.add_actor(this._contentArea); this._scrollArea.add_actor(this._contentArea);
@ -636,13 +654,49 @@ Notification.prototype = {
if (!props) if (!props)
props = {}; props = {};
props.row = 2; props.row = 2;
props.col = 1; props.col = 2;
this._table.add_style_class_name('multi-line-notification'); this._table.add_style_class_name('multi-line-notification');
this._table.add(this._actionArea, props); this._table.add(this._actionArea, props);
this._updateLastColumnSettings();
this._updated(); this._updated();
}, },
_updateLastColumnSettings: function() {
if (this._scrollArea)
this._table.child_set(this._scrollArea, { col: this._imageBin ? 2 : 1,
col_span: this._imageBin ? 1 : 2 });
if (this._actionArea)
this._table.child_set(this._actionArea, { col: this._imageBin ? 2 : 1,
col_span: this._imageBin ? 1 : 2 });
},
setImage: function(image) {
if (this._imageBin)
this.unsetImage();
this._imageBin = new St.Bin();
this._imageBin.child = image;
this._imageBin.opacity = 230;
this._table.add_style_class_name('notification-with-image');
this._updateLastColumnSettings();
this._table.add(this._imageBin, { row: 1,
col: 1,
row_span: 2,
x_expand: false,
y_expand: false,
x_fill: false,
y_fill: false });
},
unsetImage: function() {
if (this._imageBin) {
this._table.remove_style_class_name('notification-with-image');
this._table.remove_actor(this._imageBin);
this._imageBin = null;
this._updateLastColumnSettings();
}
},
// addButton: // addButton:
// @id: the action ID // @id: the action ID
// @label: the label for the action's button // @label: the label for the action's button
@ -658,7 +712,9 @@ Notification.prototype = {
let box = new St.BoxLayout({ name: 'notification-actions' }); let box = new St.BoxLayout({ name: 'notification-actions' });
this.setActionArea(box, { x_expand: false, this.setActionArea(box, { x_expand: false,
y_expand: false,
x_fill: false, x_fill: false,
y_fill: false,
x_align: St.Align.END }); x_align: St.Align.END });
this._buttonBox = box; this._buttonBox = box;
} }

View File

@ -119,11 +119,6 @@ NotificationDaemon.prototype = {
return new St.Icon({ icon_name: icon, return new St.Icon({ icon_name: icon,
icon_type: St.IconType.FULLCOLOR, icon_type: St.IconType.FULLCOLOR,
icon_size: size }); icon_size: size });
} else if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return textureCache.load_from_raw(data, hasAlpha,
width, height, rowStride, size);
} else { } else {
let stockIcon; let stockIcon;
switch (hints.urgency) { switch (hints.urgency) {
@ -220,14 +215,18 @@ NotificationDaemon.prototype = {
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true); hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Be compatible with the various hints for image data // Be compatible with the various hints for image data and image path
// 'image-data' is the latest name of this hint, introduced in 1.2 // 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
if (!hints['image-data']) {
if (!hints['image-path'] && hints['image_path'])
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
if (!hints['image-data'])
if (hints['image_data']) if (hints['image_data'])
hints['image-data'] = hints['image_data']; // version 1.1 of the spec hints['image-data'] = hints['image_data']; // version 1.1 of the spec
else if (hints['icon_data']) else if (hints['icon_data'] && !hints['image-path'])
hints['image-data'] = hints['icon_data']; // previous versions of the spec // early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
} hints['image-data'] = hints['icon_data'];
let ndata = { appName: appName, let ndata = { appName: appName,
icon: icon, icon: icon,
@ -331,6 +330,23 @@ NotificationDaemon.prototype = {
clear: true }); clear: true });
} }
if (hints['image-data'] || hints['image-path']) {
let image = null;
if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
width, height, rowStride, notification.IMAGE_SIZE);
} else if (hints['image-path']) {
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
notification.IMAGE_SIZE,
notification.IMAGE_SIZE);
}
notification.setImage(image);
} else {
notification.unsetImage();
}
if (actions.length) { if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true); notification.setUseActionIcons(hints['action-icons'] == true);
for (let i = 0; i < actions.length - 1; i += 2) { for (let i = 0; i < actions.length - 1; i += 2) {