[gnome-shell] messageTray: implement showing images in notifications



commit aabe56ba7972655c8517750d426a2bebc01891d6
Author: Neha Doijode <ndoijode gmail com>
Date:   Mon Aug 29 22:41:24 2011 +0530

    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 f73abb4..070d082 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 5be5fd6..89d3aca 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 361c585..d3321d7 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) {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]