[gnome-shell] Use a fading icon button in genericDisplay



commit 3852176e80323b9a5161e852525134df511b6782
Author: Milan Bouchet-Valat <nalimilan club fr>
Date:   Thu Jul 2 18:35:34 2009 +0200

    Use a fading icon button in genericDisplay
    
    Add a new icon button in button.js that fades in/out with a short delay when the mouse enters/leaves its parent. Use it for the information button of genericDisplay.

 js/ui/button.js         |  107 +++++++++++++++++++++++++++++++++++++++++++++++
 js/ui/genericDisplay.js |   69 +++++++++++++-----------------
 2 files changed, 136 insertions(+), 40 deletions(-)
---
diff --git a/js/ui/button.js b/js/ui/button.js
index 0bb4146..16c0bd5 100644
--- a/js/ui/button.js
+++ b/js/ui/button.js
@@ -2,6 +2,11 @@
 
 const Big = imports.gi.Big;
 const Clutter = imports.gi.Clutter;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+
+const Shell = imports.gi.Shell;
+const Tweener = imports.ui.tweener;
 
 const DEFAULT_BUTTON_COLOR = new Clutter.Color();
 DEFAULT_BUTTON_COLOR.from_pixel(0xeeddcc66);
@@ -112,3 +117,105 @@ Button.prototype = {
         }
     }
 };
+
+/* Delay before the icon should appear, in seconds after the pointer has entered the parent */
+const SHOW_ICON_DELAY = 250; // 0.25 second
+const ANIMATION_TIME = 0.25;
+
+/* This is an icon button that fades in/out when mouse enters/leaves the parent.
+ * A delay is used before the fading starts. You can force it to be shown if needed.
+ *
+ * parent -- used to show/hide the button depending on mouse entering/leaving it
+ * size -- size in pixels of  both the button and the icon it contains
+ * texture -- optional, must be used if the texture for the icon is already created (else, use setIconFromName)
+ */
+function iconButton(parent, size, icon) {
+    this._init(parent, size, icon);
+}
+
+iconButton.prototype = {
+    _init : function(parent, size, texture) {
+        this._size = size;
+        if(texture)
+            this.actor = texture;
+        else
+            this.actor = new Clutter.Texture({ width: this._size, height: this._size });
+        this.actor.set_reactive(true);
+        this.actor.set_opacity(0);
+        parent.connect("enter-event", Lang.bind(this, function(actor, event) {
+            this._shouldHide = false;
+
+            // Nothing to do if the cursor has come back from a child of the parent actor
+            if(actor.get_children().indexOf(Shell.get_event_related(event)) != -1)
+                return;
+
+            this._sourceId = Mainloop.timeout_add(SHOW_ICON_DELAY,
+                                                  Lang.bind(this, this._fadeIn));
+        }));
+        parent.connect("leave-event", Lang.bind(this, function(actor, event) {
+            // Nothing to do if the cursor has merely entered a child of the parent actor
+            if(actor.get_children().indexOf(Shell.get_event_related(event)) != -1)
+                return;
+
+            // Remember that we should not be visible to hide the button if forceShow is unset
+            if(this._forceShow) {
+                this._shouldHide = true;
+                return;
+            }
+
+            this._fadeOut()
+        }));
+    },
+
+    /// Private methods ///
+
+    setIconFromName : function(iconName) {
+        let iconTheme = Gtk.IconTheme.get_default();
+        let iconInfo = iconTheme.lookup_icon(iconName, this._size, 0);
+        if (!iconInfo)
+            return;
+
+        let iconPath = iconInfo.get_filename();
+        this.actor.set_from_file(iconPath);
+    },
+
+    // Useful if we want to show the button immediately,
+    // e.g. in case the mouse is already in the parent when the button is created
+    show : function() {
+        this.actor.set_opacity(255);
+    },
+
+    // If show is true, prevents the button from fading out
+    forceShow : function(show) {
+        this._forceShow = show;
+        // Hide the button if it should have been hidden under normal conditions
+        if(!this._forceShow && this._shouldHide) {
+           this._fadeOut();
+        }
+    },
+
+    /// Private methods ///
+
+    _fadeIn : function() {
+        if(this._sourceId) {
+            Mainloop.source_remove(this._sourceId);
+            this._sourceId = null;
+        }
+        Tweener.removeTweens(this.actor);
+        Tweener.addTween(this.actor, { opacity: 255,
+                                       time: ANIMATION_TIME,
+                                       transition :"easeInQuad" });
+    },
+
+    _fadeOut : function() {
+        if(this._sourceId) {
+            Mainloop.source_remove(this._sourceId);
+            this._sourceId = null;
+        }
+        Tweener.removeTweens(this.actor);
+        Tweener.addTween(this.actor, { opacity: 0,
+                                       time: ANIMATION_TIME,
+                                       transition :"easeOutQuad" });
+    }
+};
+
diff --git a/js/ui/genericDisplay.js b/js/ui/genericDisplay.js
index e6285f7..56dc4eb 100644
--- a/js/ui/genericDisplay.js
+++ b/js/ui/genericDisplay.js
@@ -12,6 +12,7 @@ const Signals = imports.signals;
 const Shell = imports.gi.Shell;
 const Tidy = imports.gi.Tidy;
 
+const Button = imports.ui.button;
 const DND = imports.ui.dnd;
 const Link = imports.ui.link;
 
@@ -84,33 +85,30 @@ GenericDisplayItem.prototype = {
 
         let global = Shell.Global.get();
         let infoIconUri = "file://" + global.imagedir + "info.svg";
-
-        this._informationButton = Shell.TextureCache.get_default().load_uri_sync(infoIconUri, 
-                                                                                 INFORMATION_BUTTON_SIZE,
-                                                                                 INFORMATION_BUTTON_SIZE);
-
-        this._informationButton.x = availableWidth - ITEM_DISPLAY_PADDING_RIGHT - INFORMATION_BUTTON_SIZE;
-        this._informationButton.y = ITEM_DISPLAY_HEIGHT / 2 - INFORMATION_BUTTON_SIZE / 2; 
-        this._informationButton.reactive = true;
+        let infoIcon = Shell.TextureCache.get_default().load_uri_sync(infoIconUri,
+                                                                      INFORMATION_BUTTON_SIZE,
+                                                                      INFORMATION_BUTTON_SIZE);
+        this._informationButton = new Button.iconButton(this.actor, INFORMATION_BUTTON_SIZE, infoIcon);
+        this._informationButton.actor.x = availableWidth - ITEM_DISPLAY_PADDING_RIGHT - INFORMATION_BUTTON_SIZE;
+        this._informationButton.actor.y = ITEM_DISPLAY_HEIGHT / 2 - INFORMATION_BUTTON_SIZE / 2;
 
         // Connecting to the button-press-event for the information button ensures that the actor, 
         // which is a draggable actor, does not get the button-press-event and doesn't initiate
         // the dragging, which then prevents us from getting the button-release-event for the button.
-        this._informationButton.connect('button-press-event', 
-                                        Lang.bind(this,
-                                                  function() {
-                                                      return true;
-                                                  }));
-        this._informationButton.connect('button-release-event',
-                                        Lang.bind(this, 
-                                                  function() {
-                                                      // Selects the item by highlighting it and displaying its details
-                                                      this.emit('select');
-                                                      return true;  
-                                                  }));
-        this._informationButton.hide();
-        this.actor.add_actor(this._informationButton);
-        this._informationButton.lower_bottom();
+        this._informationButton.actor.connect('button-press-event',
+                                              Lang.bind(this,
+                                                        function() {
+                                                            return true;
+                                                        }));
+        this._informationButton.actor.connect('button-release-event',
+                                              Lang.bind(this,
+                                                        function() {
+                                                            // Selects the item by highlighting it and displaying its details
+                                                            this.emit('select');
+                                                            return true;
+                                                        }));
+        this.actor.add_actor(this._informationButton.actor);
+        this._informationButton.actor.lower_bottom();
 
         this._name = null;
         this._description = null;
@@ -118,9 +116,6 @@ GenericDisplayItem.prototype = {
         this._previewIcon = null; 
 
         this.dragActor = null;
-
-        this.actor.connect('enter-event', Lang.bind(this, this._onEnter));
-        this.actor.connect('leave-event', Lang.bind(this, this._onLeave));
     },
 
     //// Draggable object interface ////
@@ -155,17 +150,21 @@ GenericDisplayItem.prototype = {
 
     // Shows the information button when the item was drawn under the mouse pointer.
     onDrawnUnderPointer: function() {
-        this._informationButton.show();  
+        this._informationButton.show();
     },
 
     // Highlights the item by setting a different background color than the default 
     // if isSelected is true, removes the highlighting otherwise.
     markSelected: function(isSelected) {
        let color;
-       if (isSelected)
+       if (isSelected) {
            color = ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR;
-       else
+           this._informationButton.forceShow(true);
+       }
+       else {
            color = ITEM_DISPLAY_BACKGROUND_COLOR;
+           this._informationButton.forceShow(false);
+       }
        this._bg.background_color = color;
     },
 
@@ -308,21 +307,11 @@ GenericDisplayItem.prototype = {
 
     //// Private methods ////
 
-    // Performs actions on mouse enter event for the item. Currently, shows the information button for the item.
-    _onEnter: function(actor, event) {
-        this._informationButton.show();
-    },
-
-    // Performs actions on mouse leave event for the item. Currently, hides the information button for the item.
-    _onLeave: function(actor, event) {
-        this._informationButton.hide();
-    },
-
     // Hides the information button once the item starts being dragged.
     _onDragBegin : function (draggable, time) {
         // For some reason, we are not getting leave-event signal when we are dragging an item,
         // so we should remove the link manually.
-        this._informationButton.hide();
+        this._informationButton.actor.hide();
     } 
 };
 



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