Blob Blame History Raw
From a50ac2fafad6bb668d60a50dd4450b056233f505 Mon Sep 17 00:00:00 2001
From: Michael Catanzaro <mcatanzaro@igalia.com>
Date: Thu, 26 Feb 2015 14:05:28 -0600
Subject: [PATCH 10/15] nautilus-floating-bar: hide on hover

Hide on hover work is by Nelson Benitez

The update also includes some style tweaks from Cosimo.

A couple ephy-specific changes are tagged EPIPHANY

Upstream status: This patch needs to be cleaned up significantly before
it can go upstream. We don't want any of the unrelated changes. There's
no value in syncing this file with nautilus because they're going to
delete theirs.

https://bugzilla.gnome.org/show_bug.cgi?id=651293
https://bugzilla.gnome.org/show_bug.cgi?id=742590
---
 embed/ephy-embed.c                  |   4 +-
 lib/widgets/nautilus-floating-bar.c | 263 ++++++++++++++++++++++++++++++------
 lib/widgets/nautilus-floating-bar.h |  11 +-
 3 files changed, 233 insertions(+), 45 deletions(-)

diff --git a/embed/ephy-embed.c b/embed/ephy-embed.c
index 798bbc4..f1f3227 100644
--- a/embed/ephy-embed.c
+++ b/embed/ephy-embed.c
@@ -716,8 +716,8 @@ ephy_embed_constructed (GObject *object)
   gtk_overlay_add_overlay (GTK_OVERLAY (overlay), priv->fullscreen_message_label);
   ephy_embed_set_fullscreen_message (embed, FALSE);
 
-  /* statusbar is hidden by default */
-  priv->floating_bar = nautilus_floating_bar_new (NULL, NULL, FALSE);
+  /* statusbar is hidden by default, hide on hover is on */
+  priv->floating_bar = nautilus_floating_bar_new (NULL, NULL, FALSE, TRUE);
   gtk_widget_set_halign (priv->floating_bar, GTK_ALIGN_START);
   gtk_widget_set_valign (priv->floating_bar, GTK_ALIGN_END);
   gtk_widget_set_no_show_all (priv->floating_bar, TRUE);
diff --git a/lib/widgets/nautilus-floating-bar.c b/lib/widgets/nautilus-floating-bar.c
index 0eff290..8d789c2 100644
--- a/lib/widgets/nautilus-floating-bar.c
+++ b/lib/widgets/nautilus-floating-bar.c
@@ -15,9 +15,7 @@
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Authors: Cosimo Cecchi <cosimoc@redhat.com>
  *
@@ -29,6 +27,8 @@
 
 #include "nautilus-floating-bar.h"
 
+#define HOVER_HIDE_TIMEOUT_INTERVAL 250
+
 struct _NautilusFloatingBarDetails {
 	gchar *primary_label;
 	gchar *details_label;
@@ -38,12 +38,15 @@ struct _NautilusFloatingBarDetails {
 	GtkWidget *spinner;
 	gboolean show_spinner;
 	gboolean is_interactive;
+	gboolean hover_hide;
+	guint hover_timeout_id;
 };
 
 enum {
 	PROP_PRIMARY_LABEL = 1,
 	PROP_DETAILS_LABEL,
 	PROP_SHOW_SPINNER,
+	PROP_HOVER_HIDE,
 	NUM_PROPERTIES
 };
 
@@ -99,6 +102,9 @@ nautilus_floating_bar_get_property (GObject *object,
 	case PROP_SHOW_SPINNER:
 		g_value_set_boolean (value, self->priv->show_spinner);
 		break;
+	case PROP_HOVER_HIDE:
+		g_value_set_boolean (value, self->priv->hover_hide);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 		break;
@@ -123,6 +129,9 @@ nautilus_floating_bar_set_property (GObject *object,
 	case PROP_SHOW_SPINNER:
 		nautilus_floating_bar_set_show_spinner (self, g_value_get_boolean (value));
 		break;
+	case PROP_HOVER_HIDE:
+		self->priv->hover_hide = g_value_get_boolean (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 		break;
@@ -148,30 +157,140 @@ update_labels (NautilusFloatingBar *self)
 	gtk_widget_set_visible (self->priv->details_label_widget, details_visible);
 }
 
+static void
+nautilus_floating_bar_show (GtkWidget *widget)
+{
+	NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (widget);
+
+	// EPIPHANY: don't want to unhide the widget if it's going to appear on the top of the screen
+	if (gtk_widget_get_valign (widget) == GTK_ALIGN_START)
+		return;
+
+	GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->show (widget);
+
+	if (self->priv->show_spinner) {
+		gtk_spinner_start (GTK_SPINNER (self->priv->spinner));
+	}
+}
+
+static void
+nautilus_floating_bar_hide (GtkWidget *widget)
+{
+	NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (widget);
+
+	GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->hide (widget);
+
+	gtk_spinner_stop (GTK_SPINNER (self->priv->spinner));
+}
+
+void
+nautilus_floating_bar_remove_hover_timeout (NautilusFloatingBar *self)
+{
+	if (self->priv->hover_timeout_id != 0) {
+		g_source_remove (self->priv->hover_timeout_id);
+		self->priv->hover_timeout_id = 0;
+	}
+}
+
+typedef struct {
+	GtkWidget *overlay;
+	GtkWidget *floating_bar;
+	GdkDevice *device;
+	gint y_down_limit;
+	gint y_upper_limit;
+	gboolean first_time;
+} CheckPointerData;
+
+static void
+check_pointer_data_free (gpointer data)
+{
+	g_slice_free (CheckPointerData, data);
+}
+
+static gboolean
+check_pointer_timeout (gpointer user_data)
+{
+  CheckPointerData *data = user_data;
+  gint pointer_y = -1;
+
+  gdk_window_get_device_position (gtk_widget_get_window (data->overlay), data->device,
+                                  NULL, &pointer_y, NULL);
+
+  if (pointer_y == -1 || pointer_y < data->y_down_limit || pointer_y > data->y_upper_limit) {
+	if (! data->first_time) {
+		gtk_widget_set_valign (data->floating_bar, GTK_ALIGN_END);
+		nautilus_floating_bar_show (data->floating_bar);
+	}
+	NAUTILUS_FLOATING_BAR (data->floating_bar)->priv->hover_timeout_id = 0;
+
+	return G_SOURCE_REMOVE;
+
+  } else if (data->first_time) {
+	//hide floating bar at top position of widget
+	nautilus_floating_bar_hide (data->floating_bar);
+	gtk_widget_set_valign (data->floating_bar, GTK_ALIGN_START);
+	gtk_widget_queue_resize (data->floating_bar);
+  }
+
+  if (data->first_time) {
+	data->first_time = FALSE;
+  }
+
+  return G_SOURCE_CONTINUE;
+}
+
 static gboolean
 overlay_enter_notify_cb (GtkWidget        *parent,
 			 GdkEventCrossing *event,
 			 gpointer          user_data)
 {
 	GtkWidget *widget = user_data;
+	CheckPointerData *data;
+	GtkAllocation alloc_parent;
+	gint y_pos;
+
+	NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (widget);
+
+	if (self->priv->hover_timeout_id != 0) {
+		g_source_remove (self->priv->hover_timeout_id);
+	}
 
 	if (event->window != gtk_widget_get_window (widget)) {
-		return FALSE;
+		return GDK_EVENT_PROPAGATE;
 	}
 
 	if (NAUTILUS_FLOATING_BAR (widget)->priv->is_interactive) {
-		return FALSE;
+		return GDK_EVENT_PROPAGATE;
 	}
 
-	if (gtk_widget_get_halign (widget) == GTK_ALIGN_START) {
-		gtk_widget_set_halign (widget, GTK_ALIGN_END);
-	} else {
-		gtk_widget_set_halign (widget, GTK_ALIGN_START);
+	if (! self->priv->hover_hide) {
+		if (gtk_widget_get_halign (widget) == GTK_ALIGN_START) {
+			gtk_widget_set_halign (widget, GTK_ALIGN_END);
+		} else {
+			gtk_widget_set_halign (widget, GTK_ALIGN_START);
+		}
+
+		return GDK_EVENT_PROPAGATE;
 	}
 
-	gtk_widget_queue_resize (widget);
+	gtk_widget_get_allocation (parent, &alloc_parent);
+	gdk_window_get_position (gtk_widget_get_window (widget), NULL, &y_pos);
 
-	return FALSE;
+	data = g_slice_new (CheckPointerData);
+	data->overlay = parent;
+	data->floating_bar = widget;
+	data->device = gdk_event_get_device ((GdkEvent *) event);
+	data->y_down_limit = y_pos;
+	data->y_upper_limit = alloc_parent.height;
+	data->first_time = TRUE;
+
+	self->priv->hover_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, HOVER_HIDE_TIMEOUT_INTERVAL,
+								check_pointer_timeout, data,
+								check_pointer_data_free);
+
+	g_source_set_name_by_id (self->priv->hover_timeout_id, "[nautilus-floating-bar] overlay_enter_notify_cb");
+
+	return GDK_EVENT_STOP;
 }
 
 static void
@@ -194,49 +313,94 @@ nautilus_floating_bar_parent_set (GtkWidget *widget,
 }
 
 static void
-nautilus_floating_bar_show (GtkWidget *widget)
+get_padding_and_border (GtkWidget *widget,
+                        GtkBorder *border)
 {
-	NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (widget);
+  GtkStyleContext *context;
+  GtkStateFlags state;
+  GtkBorder tmp;
+
+  context = gtk_widget_get_style_context (widget);
+  state = gtk_widget_get_state_flags (widget);
+
+  gtk_style_context_get_padding (context, state, border);
+  gtk_style_context_get_border (context, state, &tmp);
+  border->top += tmp.top;
+  border->right += tmp.right;
+  border->bottom += tmp.bottom;
+  border->left += tmp.left;
+}
 
-	GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->show (widget);
+static void
+nautilus_floating_bar_get_preferred_width (GtkWidget *widget,
+					   gint      *minimum_size,
+					   gint      *natural_size)
+{
+	GtkBorder border;
 
-	if (self->priv->show_spinner) {
-		gtk_spinner_start (GTK_SPINNER (self->priv->spinner));
-	}
+	get_padding_and_border (widget, &border);
+
+	GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->get_preferred_width (widget,
+										    minimum_size,
+										    natural_size);
+
+	*minimum_size += border.left + border.right;
+	*natural_size += border.left + border.right;
 }
 
 static void
-nautilus_floating_bar_hide (GtkWidget *widget)
+nautilus_floating_bar_get_preferred_width_for_height (GtkWidget *widget,
+						      gint       height,
+						      gint      *minimum_size,
+						      gint      *natural_size)
 {
-	NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (widget);
+	GtkBorder border;
 
-	GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->hide (widget);
+	get_padding_and_border (widget, &border);
 
-	gtk_spinner_stop (GTK_SPINNER (self->priv->spinner));
+	GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->get_preferred_width_for_height (widget,
+											       height,
+											       minimum_size,
+											       natural_size);
+
+	*minimum_size += border.left + border.right;
+	*natural_size += border.left + border.right;
 }
 
-static gboolean
-nautilus_floating_bar_draw (GtkWidget *widget,
-			    cairo_t *cr)
+static void
+nautilus_floating_bar_get_preferred_height (GtkWidget *widget,
+					    gint      *minimum_size,
+					    gint      *natural_size)
 {
-	GtkStyleContext *context;
+	GtkBorder border;
 
-	context = gtk_widget_get_style_context (widget);
+	get_padding_and_border (widget, &border);
 
-	gtk_style_context_save (context);
-	gtk_style_context_set_state (context, gtk_widget_get_state_flags (widget));
+	GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->get_preferred_height (widget,
+										     minimum_size,
+										     natural_size);
 
-	gtk_render_background (context, cr, 0, 0,
-			       gtk_widget_get_allocated_width (widget),
-			       gtk_widget_get_allocated_height (widget));
+	*minimum_size += border.top + border.bottom;
+	*natural_size += border.top + border.bottom;
+}
+
+static void
+nautilus_floating_bar_get_preferred_height_for_width (GtkWidget *widget,
+						      gint       width,
+						      gint      *minimum_size,
+						      gint      *natural_size)
+{
+	GtkBorder border;
 
-	gtk_render_frame (context, cr, 0, 0,
-			  gtk_widget_get_allocated_width (widget),
-			  gtk_widget_get_allocated_height (widget));
+	get_padding_and_border (widget, &border);
 
-	gtk_style_context_restore (context);
+	GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->get_preferred_height_for_width (widget,
+											       width,
+											       minimum_size,
+											       natural_size);
 
-	return GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->draw (widget, cr);;
+	*minimum_size += border.top + border.bottom;
+	*natural_size += border.top + border.bottom;
 }
 
 static void
@@ -255,7 +419,7 @@ nautilus_floating_bar_constructed (GObject *obj)
 	self->priv->spinner = w;
 
 	gtk_widget_set_size_request (w, 16, 16);
-	gtk_widget_set_margin_left (w, 8);
+	gtk_widget_set_margin_start (w, 8);
 
 	labels_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 	gtk_box_pack_start (GTK_BOX (box), labels_box, TRUE, TRUE, 0);
@@ -304,7 +468,10 @@ nautilus_floating_bar_class_init (NautilusFloatingBarClass *klass)
 	oclass->get_property = nautilus_floating_bar_get_property;
 	oclass->finalize = nautilus_floating_bar_finalize;
 
-	wclass->draw = nautilus_floating_bar_draw;
+	wclass->get_preferred_width = nautilus_floating_bar_get_preferred_width;
+	wclass->get_preferred_width_for_height = nautilus_floating_bar_get_preferred_width_for_height;
+	wclass->get_preferred_height = nautilus_floating_bar_get_preferred_height;
+	wclass->get_preferred_height_for_width = nautilus_floating_bar_get_preferred_height_for_width;
 	wclass->show = nautilus_floating_bar_show;
 	wclass->hide = nautilus_floating_bar_hide;
 	wclass->parent_set = nautilus_floating_bar_parent_set;
@@ -328,6 +495,14 @@ nautilus_floating_bar_class_init (NautilusFloatingBarClass *klass)
 				      FALSE,
 				      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
+	properties[PROP_HOVER_HIDE] =
+		g_param_spec_boolean ("hover-hide",
+				      "Hide bar on hover",
+				      "Whether the floating bar should be hidden on hover",
+				      TRUE,
+				      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+				      G_PARAM_STATIC_STRINGS);
+
 	signals[ACTION] =
 		g_signal_new ("action",
 			      G_TYPE_FROM_CLASS (klass),
@@ -347,6 +522,7 @@ nautilus_floating_bar_set_primary_label (NautilusFloatingBar *self,
 {
 	if (g_strcmp0 (self->priv->primary_label, label) != 0) {
 		g_free (self->priv->primary_label);
+		// EPIPHANY: The URI is stored escaped
 		self->priv->primary_label = g_uri_unescape_string (label, NULL);
 
 		g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRIMARY_LABEL]);
@@ -391,15 +567,23 @@ nautilus_floating_bar_set_show_spinner (NautilusFloatingBar *self,
 	}
 }
 
+gboolean
+nautilus_floating_bar_is_hover_hide (NautilusFloatingBar *self)
+{
+	return self->priv->hover_hide;
+}
+
 GtkWidget *
 nautilus_floating_bar_new (const gchar *primary_label,
 			   const gchar *details_label,
-			   gboolean show_spinner)
+			   gboolean show_spinner,
+			   gboolean hover_hide)
 {
 	return g_object_new (NAUTILUS_TYPE_FLOATING_BAR,
 			     "primary-label", primary_label,
 			     "details-label", details_label,
 			     "show-spinner", show_spinner,
+			     "hover-hide", hover_hide,
 			     "orientation", GTK_ORIENTATION_HORIZONTAL,
 			     "spacing", 8,
 			     NULL);
@@ -416,6 +600,7 @@ nautilus_floating_bar_add_action (NautilusFloatingBar *self,
 	gtk_widget_show (w);
 
 	button = gtk_button_new ();
+	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
 	gtk_button_set_image (GTK_BUTTON (button), w);
 	gtk_box_pack_end (GTK_BOX (self), button, FALSE, FALSE, 0);
 	gtk_widget_show (button);
diff --git a/lib/widgets/nautilus-floating-bar.h b/lib/widgets/nautilus-floating-bar.h
index ba31ac5..44ab659 100644
--- a/lib/widgets/nautilus-floating-bar.h
+++ b/lib/widgets/nautilus-floating-bar.h
@@ -15,9 +15,7 @@
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Authors: Cosimo Cecchi <cosimoc@redhat.com>
  *
@@ -60,7 +58,8 @@ GType       nautilus_floating_bar_get_type  (void);
 
 GtkWidget * nautilus_floating_bar_new              (const gchar *primary_label,
 						    const gchar *details_label,
-						    gboolean show_spinner);
+						    gboolean show_spinner,
+						    gboolean hover_hide);
 
 void       nautilus_floating_bar_set_primary_label (NautilusFloatingBar *self,
 						    const gchar *label);
@@ -77,5 +76,9 @@ void        nautilus_floating_bar_add_action       (NautilusFloatingBar *self,
 						    gint action_id);
 void        nautilus_floating_bar_cleanup_actions  (NautilusFloatingBar *self);
 
+void        nautilus_floating_bar_remove_hover_timeout (NautilusFloatingBar *self);
+
+gboolean    nautilus_floating_bar_is_hover_hide (NautilusFloatingBar *self);
+
 #endif /* __NAUTILUS_FLOATING_BAR_H__ */
 
-- 
2.1.0