[libegg/spread-table-dnd] Committing initial version of animated drag'n'drop for EggSpreadTableDnd



commit 8d0c7540626b6e57135db18a8c4184c711f46876
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Fri Apr 1 17:18:41 2011 +0900

    Committing initial version of animated drag'n'drop for EggSpreadTableDnd
    
    This commit breaks the Dnd implementation in many ways and the implementation
    needs finishing, however with this commit you can see that the animated
    drag'n'drop "drop target highlighting" experience is working.

 libegg/spreadtable/eggspreadtablednd.c |  362 +++++++++++++++++++++++---------
 1 files changed, 263 insertions(+), 99 deletions(-)
---
diff --git a/libegg/spreadtable/eggspreadtablednd.c b/libegg/spreadtable/eggspreadtablednd.c
index f8f520e..72388a2 100644
--- a/libegg/spreadtable/eggspreadtablednd.c
+++ b/libegg/spreadtable/eggspreadtablednd.c
@@ -24,6 +24,7 @@
 #include <gtk/gtk.h>
 #include <string.h>
 #include "eggspreadtablednd.h"
+#include "eggplaceholder.h"
 
 #define DEFAULT_LINES 2
 #define P_(msgid) (msgid)
@@ -86,6 +87,9 @@ static void          egg_spread_table_dnd_insert_child (EggSpreadTable    *sprea
 							gint               index);
 
 /* Drag and Drop callbacks & other utilities */
+static void          drag_begin                        (GtkWidget         *widget,
+							GdkDragContext    *context,
+							EggSpreadTableDnd *spread_table);
 static void          drag_data_get                     (GtkWidget         *widget,
 							GdkDragContext    *context,
 							GtkSelectionData  *selection,
@@ -97,23 +101,33 @@ static GtkWidget    *get_drag_child                    (EggSpreadTableDnd *sprea
 static GtkWidget    *get_child_at_position             (EggSpreadTableDnd *spread_table,
 							gint               x,
 							gint               y,
-							gboolean          *before);
-static void          cancel_drag                       (EggSpreadTableDnd *spread_table);
+							gint              *index);
+static gint          get_index_at_position             (EggSpreadTableDnd *spread_table,
+							gint               x,
+							gint               y,
+							gint              *line_ret);
 
 
-static gboolean      drag_highlight_draw               (GtkWidget         *widget,
-							cairo_t           *cr,
-							gpointer           data);
-static void          highlight_drag                    (GtkWidget         *widget, 
-							gboolean           before);
-static void          unhighlight_drag                  (GtkWidget         *widget);
+static void          cancel_drag                       (EggSpreadTableDnd *spread_table);
 
 
 struct _EggSpreadTableDndPrivate {
-  GtkWidget *drop_target;  /* Remember which child widget has the drop highlight */
+  GtkWidget *drop_target;  /* Remember which child widget is the active placeholder */
+  gint       drop_index;
+
+  GtkWidget *drag_child;   /* The widget that is currently being dragged */
+
+  GList     *placeholders; /* A list of placeholders that may be present while a drag operation
+			    * is active and the mouse is over this spread table (or presently disappearing
+			    * after the mouse has left the area) */
+
+  gint       drag_origin;  /* The original index of the drag child when the drag began (this is captured
+			    * in order to restore the original child in the case of a failed drag).
+			    */
+
+  gint       drag_child_width;  /* The original size of the drag child */
+  gint       drag_child_height;
 
-  GtkWidget *drag_child;   /* The widget that is currently being dragged, if the widget has
-			    * no window and the SpreadTable is the active drag source */
   gint pressed_button;
   gint press_start_x;
   gint press_start_y;
@@ -129,7 +143,6 @@ static const GtkTargetEntry dnd_targets[] = {
   { "application/x-egg-spread-table-dnd-child", GTK_TARGET_SAME_APP, 0 }
 };
 
-
 G_DEFINE_TYPE (EggSpreadTableDnd, egg_spread_table_dnd, EGG_TYPE_SPREAD_TABLE)
 
 static void
@@ -354,11 +367,55 @@ egg_spread_table_dnd_drag_leave (GtkWidget         *widget,
 {
   EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
 
-  /* Cancel the drag here also, so that highlights go away when
-   * leaving the target spread table */
   cancel_drag (spread_table);
 }
 
+
+static void
+adjust_line_segment (EggSpreadTableDnd *spread_table,
+		     gint               line,
+		     gint               offset)
+{
+  gint *segments;
+
+  segments = egg_spread_table_get_segments (EGG_SPREAD_TABLE (spread_table));
+  egg_spread_table_set_segment_length (EGG_SPREAD_TABLE (spread_table), 
+					       line, segments[line] + offset);
+  g_free (segments);
+}
+
+static void
+placeholder_animated_out (GtkWidget         *placeholder,
+			  EggSpreadTableDnd *spread_table)
+{
+  gint placeholder_position;
+  gint size, line;
+  
+  if (gtk_orientable_get_orientation (GTK_ORIENTABLE (spread_table)) == GTK_ORIENTATION_VERTICAL)
+    size = gtk_widget_get_allocated_width (GTK_WIDGET (spread_table));
+  else
+    size = gtk_widget_get_allocated_height (GTK_WIDGET (spread_table));
+
+  line = egg_spread_table_get_child_line (EGG_SPREAD_TABLE (spread_table),
+					  placeholder, size);
+
+
+  /* After animating out a placeholder, remove the placeholder
+   * and possibly adjust the current drop target index 
+   */
+  gtk_container_child_get (GTK_CONTAINER (spread_table), placeholder,
+			   "position", &placeholder_position, 
+			   NULL);
+
+  if (placeholder_position < spread_table->priv->drop_index)
+    spread_table->priv->drop_index--;
+
+  /* Adjust line segment here manually since table may be locked */
+  adjust_line_segment (spread_table, line, -1);
+
+  gtk_container_remove (GTK_CONTAINER (spread_table), placeholder);
+}
+
 static gboolean
 egg_spread_table_dnd_drag_motion (GtkWidget         *widget,
 				  GdkDragContext    *context,
@@ -367,18 +424,44 @@ egg_spread_table_dnd_drag_motion (GtkWidget         *widget,
 				  guint              time_)
 {
   EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
-  GtkWidget         *drop_target;
-  gboolean           before;
+  gint               index, line;
 
-  drop_target = get_child_at_position (spread_table, x, y, &before);
+  index = get_index_at_position (spread_table, x, y, &line);
 
-  if (spread_table->priv->drop_target)
-    unhighlight_drag (spread_table->priv->drop_target);
+  g_print ("[drag motion] index for position returned is %d\n", index);
 
-  spread_table->priv->drop_target = drop_target;
+  if (/* !EGG_IS_PLACEHOLDER (child) && */
+      index != spread_table->priv->drop_index)
+    {
+      if (spread_table->priv->drop_target)
+	{
+	  egg_placeholder_animate_out (EGG_PLACEHOLDER (spread_table->priv->drop_target),
+				       gtk_orientable_get_orientation (GTK_ORIENTABLE (spread_table)));
 
-  if (spread_table->priv->drop_target)
-    highlight_drag (spread_table->priv->drop_target, before);
+	  g_signal_connect (spread_table->priv->drop_target, "animation-done",
+			    G_CALLBACK (placeholder_animated_out), spread_table);
+
+	  spread_table->priv->drop_target = NULL;
+
+	}
+
+      /* The drop index is +1 because we add a placeholder here */
+      spread_table->priv->drop_index = index;
+
+      if (index >= 0)
+	{
+	  adjust_line_segment (spread_table, line, 1);
+
+	  spread_table->priv->drop_target = 
+	    egg_placeholder_new (spread_table->priv->drag_child_width, 
+				 spread_table->priv->drag_child_height);
+	  egg_spread_table_insert_child (EGG_SPREAD_TABLE (spread_table), 
+					 spread_table->priv->drop_target, index);
+
+	  egg_placeholder_animate_in (EGG_PLACEHOLDER (spread_table->priv->drop_target),
+				      gtk_orientable_get_orientation (GTK_ORIENTABLE (spread_table)));
+	}
+    }
 
   return FALSE;
 }
@@ -392,7 +475,6 @@ egg_spread_table_dnd_drag_drop (GtkWidget         *widget,
 {
   EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
 
-  /* Cancel the drag here too (otherwise the highlight can remain) */
   cancel_drag (spread_table);
 
   return FALSE;
@@ -466,6 +548,7 @@ egg_spread_table_dnd_remove (GtkContainer *container,
 {
   /* Disconnect dnd */
   g_signal_handlers_disconnect_by_func (child, G_CALLBACK (drag_data_get), container);
+  g_signal_handlers_disconnect_by_func (child, G_CALLBACK (drag_begin), container);
   gtk_drag_source_unset (child);
   
   GTK_CONTAINER_CLASS (egg_spread_table_dnd_parent_class)->remove (container, child);
@@ -489,11 +572,52 @@ egg_spread_table_dnd_insert_child (EggSpreadTable *spread_table,
 
   g_signal_connect (child, "drag-data-get", 
 		    G_CALLBACK (drag_data_get), spread_table);
+  g_signal_connect (child, "drag-begin", 
+		    G_CALLBACK (drag_begin), spread_table);
 }
 
 /*****************************************************
  *       Drag'n'Drop signals & other functions       *
  *****************************************************/
+
+static void
+drag_begin (GtkWidget         *widget,
+	    GdkDragContext    *context, 
+	    EggSpreadTableDnd *spread_table)
+{
+  GtkAllocation   allocation;
+
+  /* Hold a ref because we're going to play with the widget out of hierarchy */
+  spread_table->priv->drag_child = g_object_ref (widget);
+
+  egg_spread_table_lock (EGG_SPREAD_TABLE (spread_table));
+
+
+  /* Save the drag origin in case of failed drags and insert a placeholder as the first 
+   * default drop target */
+  gtk_container_child_get (GTK_CONTAINER (spread_table), 
+			   spread_table->priv->drag_child, 
+			   "position", &spread_table->priv->drag_origin,
+			   NULL);
+  spread_table->priv->drop_index = spread_table->priv->drag_origin;
+
+  /* Create a placeholder of the correct dimentions and insert it at the drag origin */
+  gtk_widget_get_allocation (widget, &allocation);
+  spread_table->priv->drop_target = egg_placeholder_new (allocation.width, allocation.height);
+  egg_spread_table_insert_child (EGG_SPREAD_TABLE (spread_table), 
+				 spread_table->priv->drop_target, spread_table->priv->drop_index);
+
+  spread_table->priv->drag_child_width  = allocation.width;
+  spread_table->priv->drag_child_height = allocation.height;
+
+  /* Remove the drag child */
+  gtk_container_remove (GTK_CONTAINER (spread_table), spread_table->priv->drag_child);
+
+  g_print ("[child %p] Drag began at index %d, added placeholder there with width %d and height %d\n", 
+	   widget, spread_table->priv->drag_origin, allocation.width, allocation.height);
+
+}
+
 static void
 drag_data_get (GtkWidget         *widget,
 	       GdkDragContext    *context,
@@ -541,25 +665,79 @@ cancel_drag (EggSpreadTableDnd *spread_table)
 {
   if (spread_table->priv->drop_target)
     {
-      unhighlight_drag (spread_table->priv->drop_target);
+      egg_placeholder_animate_out (EGG_PLACEHOLDER (spread_table->priv->drop_target),
+				   gtk_orientable_get_orientation (GTK_ORIENTABLE (spread_table)));
+
+      g_signal_connect (spread_table->priv->drop_target, "animation-done",
+			G_CALLBACK (placeholder_animated_out), spread_table);
+
       spread_table->priv->drop_target = NULL;
     }
   spread_table->priv->pressed_button = -1;
 }
 
-static GtkWidget *
-get_child_at_position (EggSpreadTableDnd *spread_table,
+static gint
+get_index_at_position (EggSpreadTableDnd *spread_table,
 		       gint               x,
 		       gint               y,
-		       gboolean          *before)
+		       gint              *line_ret)
 {
-  GtkWidget    *child, *ret_child = NULL;
-  GList        *children, *l;
-  GtkAllocation allocation;
+  EggSpreadTable *table;
+  GtkWidget      *widget, *child, *ret_child = NULL;
+  GList          *children, *l;
+  GtkAllocation   allocation;
+  gint            last_child_half = -1, anim_out_cnt = 0;
+  GtkOrientation  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (spread_table));
+  gint           *segments, lines, line = -1, i, full_size, spacing, position, line_width, first_child;
+  gint            index = -1;
+
+  widget = GTK_WIDGET (spread_table);
+  table  = EGG_SPREAD_TABLE (spread_table);
+
+  /* First find the "line" in question */
+  lines    = egg_spread_table_get_lines (table);
+  segments = egg_spread_table_get_segments (table);
+
+  gtk_widget_get_allocation (widget, &allocation);
+
+  if (orientation == GTK_ORIENTATION_VERTICAL)
+    {
+      full_size  = allocation.width;
+      spacing    = egg_spread_table_get_horizontal_spacing (table);
+      position   = x;
+    }
+  else
+    {
+      full_size = allocation.height;
+      spacing   = egg_spread_table_get_vertical_spacing (table);
+      position  = y;
+    }
+
+  line_width = (full_size - (spacing * (lines -1))) / lines;
+
+  for (i = 0; i < lines; i++)
+    {
+      gint start, end;
+
+      start = line_width * i + (spacing / 2) * i;
+      end   = start + line_width + spacing / 2;
+
+      if (position >= start && position <= end)
+	{
+	  line = i;
+	  break;
+	}
+    }
+  g_assert (line >= 0);
+
+  /* Get the first child on this line */
+  for (i = 0, first_child = 0; i < line; i++)
+    first_child += segments[i];
 
   children = gtk_container_get_children (GTK_CONTAINER (spread_table));
 
-  for (l = children; ret_child == NULL && l != NULL; l = l->next)
+  for (l = g_list_nth (children, first_child), i = 0; 
+       l != NULL && index < 0 && i < segments[line]; l = l->next)
     {
       child = l->data;
 
@@ -568,94 +746,80 @@ get_child_at_position (EggSpreadTableDnd *spread_table,
 
       gtk_widget_get_allocation (child, &allocation);
 
-      if (x >= allocation.x && x <= allocation.x + allocation.width &&
-	  y >= allocation.y && y <= allocation.y + allocation.height)
+      /* When it's a newly added placeholder that did not reach it's potential
+       * size yet, assume that it will potentially be it's full size (avoid flickering) */
+      if (EGG_IS_PLACEHOLDER (child))
 	{
-	  ret_child = child;
+	  if (egg_placeholder_get_animating (EGG_PLACEHOLDER (child)) == EGG_PLACEHOLDER_ANIM_OUT)
+	    anim_out_cnt++;
+
+	  allocation.width = spread_table->priv->drag_child_width;
+	  allocation.height = spread_table->priv->drag_child_height;
+	}
 
-	  if (before)
-	    {
-	      if (y < allocation.y + allocation.height / 2)
-		*before = TRUE;
-	      else
-		*before = FALSE;
-	    }
+      if (orientation == GTK_ORIENTATION_VERTICAL)
+	{
+	  if (y < allocation.y + allocation.height / 2)
+	    index = first_child + i;
+	}
+      else
+	{
+	  if (x < allocation.x + allocation.width / 2)
+	    index = first_child + i;
 	}
+
+      i++;
     }
 
-  g_list_free (children);
+  if (index < 0)
+    index = first_child + segments[line];
 
-  return ret_child;
-}
+  g_list_free (children);
 
+  g_free (segments);
 
-static gboolean
-drag_highlight_draw (GtkWidget *widget,
-		     cairo_t   *cr,
-		     gpointer   data)
-{
-  int width = gtk_widget_get_allocated_width (widget);
-  int height = gtk_widget_get_allocated_height (widget);
-  int y = 0;
-  gboolean before = GPOINTER_TO_INT (data);
-  GtkStyleContext *context;
-
-  if (before)
-    height = 2;
-  else
-    {
-      y = height - 2;
-      height = 2;
-    }
+  if (line_ret)
+    *line_ret = line;
 
-  context = gtk_widget_get_style_context (widget);
+  return index + anim_out_cnt;
+}
 
-  gtk_style_context_save (context);
-  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DND);
+static GtkWidget *
+get_child_at_position (EggSpreadTableDnd *spread_table,
+		       gint               x,
+		       gint               y,
+		       gint              *index)
+{
+  GtkWidget    *child, *ret_child = NULL;
+  GList        *children, *l;
+  GtkAllocation allocation;
 
-  gtk_render_frame (context, cr, 0, y, width, height);
+  children = gtk_container_get_children (GTK_CONTAINER (spread_table));
 
-  gtk_style_context_restore (context);
+  for (l = children; ret_child == NULL && l != NULL; l = l->next)
+    {
+      child = l->data;
 
-  cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
-  cairo_set_line_width (cr, 1.0);
-  cairo_rectangle (cr,
-                   0.5, y + 0.5,
-                   width - 1, height - 1);
-  cairo_stroke (cr);
+      if (!gtk_widget_get_visible (child))
+	continue;
 
-  return FALSE;
-}
+      gtk_widget_get_allocation (child, &allocation);
 
-static void 
-highlight_drag (GtkWidget  *widget, 
-		gboolean    before)
-{
-  g_return_if_fail (GTK_IS_WIDGET (widget));
+      if (x >= allocation.x && x <= allocation.x + allocation.width &&
+	  y >= allocation.y && y <= allocation.y + allocation.height)
+	{
+	  ret_child = child;
 
-  g_signal_connect_after (widget, "draw",
-			  G_CALLBACK (drag_highlight_draw),
-			  GINT_TO_POINTER (before));
+	  if (index)
+	    *index = g_list_index (children, ret_child);
+	}
+    }
 
-  gtk_widget_queue_draw (widget);
-}
+  g_list_free (children);
 
-static void 
-unhighlight_drag (GtkWidget *widget)
-{
-  g_return_if_fail (GTK_IS_WIDGET (widget));
-
-  g_signal_handlers_disconnect_by_func (widget,
-					drag_highlight_draw,
-					GINT_TO_POINTER (TRUE));
-  g_signal_handlers_disconnect_by_func (widget,
-					drag_highlight_draw,
-					GINT_TO_POINTER (FALSE));
-  
-  gtk_widget_queue_draw (widget);
+  return ret_child;
 }
 
-
 /*****************************************************
  *                       API                         *
  *****************************************************/



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