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:
parent
d227ddfc88
commit
aabe56ba79
@ -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);
|
||||||
|
@ -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,9 +444,19 @@ 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_expand: false,
|
||||||
y_fill: 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_fill: false });
|
||||||
|
|
||||||
this._titleLabel = new St.Label();
|
this._titleLabel = new St.Label();
|
||||||
this._bannerBox.add_actor(this._titleLabel);
|
this._bannerBox.add_actor(this._titleLabel);
|
||||||
this._bannerUrlHighlighter = new URLHighlighter();
|
this._bannerUrlHighlighter = new URLHighlighter();
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user