From aabe56ba7972655c8517750d426a2bebc01891d6 Mon Sep 17 00:00:00 2001 From: Neha Doijode Date: Mon, 29 Aug 2011 22:41:24 +0530 Subject: [PATCH] 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 --- data/theme/gnome-shell.css | 8 +++++ js/ui/messageTray.js | 62 +++++++++++++++++++++++++++++++++++-- js/ui/notificationDaemon.js | 38 ++++++++++++++++------- 3 files changed, 94 insertions(+), 14 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index f73abb401..070d082c8 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -1070,6 +1070,14 @@ StTooltip StLabel { 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 { -arrow-border-radius: 8px; -arrow-background-color: rgba(0,0,0,0.9); diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js index 5be5fd66a..89d3aca4f 100644 --- a/js/ui/messageTray.js +++ b/js/ui/messageTray.js @@ -396,6 +396,8 @@ function Notification(source, title, banner, params) { } Notification.prototype = { + IMAGE_SIZE: 125, + _init: function(source, title, banner, params) { this.source = source; this.urgency = Urgency.NORMAL; @@ -412,6 +414,7 @@ Notification.prototype = { this._titleDirection = St.TextDirection.NONE; this._spacing = 0; this._scrollPolicy = Gtk.PolicyType.AUTOMATIC; + this._imageBin = null; source.connect('destroy', Lang.bind(this, function (source, reason) { @@ -441,9 +444,19 @@ Notification.prototype = { this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate)); this._table.add(this._bannerBox, { row: 0, 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_fill: false }); + this._titleLabel = new St.Label(); this._bannerBox.add_actor(this._titleLabel); this._bannerUrlHighlighter = new URLHighlighter(); @@ -495,7 +508,10 @@ Notification.prototype = { this._actionArea = 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._icon = params.icon || this.source.createNotificationIcon(); @@ -559,7 +575,9 @@ Notification.prototype = { vscrollbar_policy: this._scrollPolicy, hscrollbar_policy: Gtk.PolicyType.NEVER, 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', vertical: true }); this._scrollArea.add_actor(this._contentArea); @@ -636,13 +654,49 @@ Notification.prototype = { if (!props) props = {}; props.row = 2; - props.col = 1; + props.col = 2; this._table.add_style_class_name('multi-line-notification'); this._table.add(this._actionArea, props); + this._updateLastColumnSettings(); 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: // @id: the action ID // @label: the label for the action's button @@ -658,7 +712,9 @@ Notification.prototype = { let box = new St.BoxLayout({ name: 'notification-actions' }); this.setActionArea(box, { x_expand: false, + y_expand: false, x_fill: false, + y_fill: false, x_align: St.Align.END }); this._buttonBox = box; } diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js index 361c585d7..d3321d710 100644 --- a/js/ui/notificationDaemon.js +++ b/js/ui/notificationDaemon.js @@ -119,11 +119,6 @@ NotificationDaemon.prototype = { return new St.Icon({ icon_name: icon, icon_type: St.IconType.FULLCOLOR, 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 { let stockIcon; switch (hints.urgency) { @@ -220,14 +215,18 @@ NotificationDaemon.prototype = { hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true); - // Be compatible with the various hints for image data - // 'image-data' is the latest name of this hint, introduced in 1.2 - if (!hints['image-data']) { + // Be compatible with the various hints for image data and image path + // 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2 + + 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']) hints['image-data'] = hints['image_data']; // version 1.1 of the spec - else if (hints['icon_data']) - hints['image-data'] = hints['icon_data']; // previous versions of the spec - } + else if (hints['icon_data'] && !hints['image-path']) + // 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, icon: icon, @@ -331,6 +330,23 @@ NotificationDaemon.prototype = { 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) { notification.setUseActionIcons(hints['action-icons'] == true); for (let i = 0; i < actions.length - 1; i += 2) {