From 3852176e80323b9a5161e852525134df511b6782 Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Thu, 2 Jul 2009 18:35:34 +0200 Subject: [PATCH] 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 0bb4146a2..16c0bd5f6 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 e6285f770..56dc4eb39 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(); } };