[recipes] Redo ingredients DND



commit b477aae102325f9622c8f49a6de4de2d099e3649
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu May 25 23:13:05 2017 -0400

    Redo ingredients DND
    
    Make the list itself the drop target, and implement
    better drag highlighting that hightlights just the gaps.
    
    For implemneting this, we also remove the header separators,
    and switch to using CSS borders for that purpose.

 src/gr-ingredients-viewer-row.c |   51 ++++------
 src/gr-ingredients-viewer.c     |  204 ++++++++++++++++++++++++++++++++++++++-
 src/gr-ingredients-viewer.h     |    4 +-
 src/gr-ingredients-viewer.ui    |    7 --
 src/recipes-dark.css            |    2 +-
 src/recipes-light.css           |    2 +-
 src/recipes.css                 |   32 ++++++
 7 files changed, 257 insertions(+), 45 deletions(-)
---
diff --git a/src/gr-ingredients-viewer-row.c b/src/gr-ingredients-viewer-row.c
index fd18523..47c9be1 100644
--- a/src/gr-ingredients-viewer-row.c
+++ b/src/gr-ingredients-viewer-row.c
@@ -576,6 +576,7 @@ drag_begin (GtkWidget      *widget,
         cairo_t *cr;
         GtkWidget *row;
         int x, y;
+        GtkWidget *viewer;
 
         row = gtk_widget_get_ancestor (widget, GTK_TYPE_LIST_BOX_ROW);
 
@@ -583,9 +584,9 @@ drag_begin (GtkWidget      *widget,
         surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, alloc.width, alloc.height);
         cr = cairo_create (surface);
 
-        gtk_style_context_add_class (gtk_widget_get_style_context (row), "during-dnd");
+        gtk_style_context_add_class (gtk_widget_get_style_context (row), "drag-icon");
         gtk_widget_draw (row, cr);
-        gtk_style_context_remove_class (gtk_widget_get_style_context (row), "during-dnd");
+        gtk_style_context_remove_class (gtk_widget_get_style_context (row), "drag-icon");
 
         gtk_widget_translate_coordinates (widget, row, 0, 0, &x, &y);
         cairo_surface_set_device_offset (surface, -x, -y);
@@ -593,6 +594,20 @@ drag_begin (GtkWidget      *widget,
 
         cairo_destroy (cr);
         cairo_surface_destroy (surface);
+
+        viewer = gtk_widget_get_ancestor (row, GR_TYPE_INGREDIENTS_VIEWER);
+        gr_ingredients_viewer_set_drag_row (GR_INGREDIENTS_VIEWER (viewer), row);
+}
+
+static void
+drag_end (GtkWidget      *widget,
+          GdkDragContext *context,
+          gpointer        data)
+{
+        GtkWidget *viewer;
+
+        viewer = gtk_widget_get_ancestor (widget, GR_TYPE_INGREDIENTS_VIEWER);
+        gr_ingredients_viewer_set_drag_row (GR_INGREDIENTS_VIEWER (viewer), NULL);
 }
 
 void
@@ -610,31 +625,6 @@ drag_data_get (GtkWidget        *widget,
                                 sizeof (gpointer));
 }
 
-static void
-drag_data_received (GtkWidget        *widget,
-                    GdkDragContext   *context,
-                    gint              x,
-                    gint              y,
-                    GtkSelectionData *selection_data,
-                    guint             info,
-                    guint32           time,
-                    gpointer          data)
-{
-        GtkWidget *target;
-        GtkWidget *row;
-        GtkWidget *source;
-
-        target = gtk_widget_get_ancestor (widget, GTK_TYPE_LIST_BOX_ROW);
-
-        row = (gpointer)* (gpointer*)gtk_selection_data_get_data (selection_data);
-        source = gtk_widget_get_ancestor (row, GTK_TYPE_LIST_BOX_ROW);
-
-        if (target == source)
-                return;
-
-        g_signal_emit (source, signals[MOVE], 0, target);
-}
-
 static int
 sort_func (GtkTreeModel *model,
            GtkTreeIter  *a,
@@ -780,11 +770,9 @@ setup_editable_row (GrIngredientsViewerRow *self)
 
                 gtk_drag_source_set (self->drag_handle, GDK_BUTTON1_MASK, entries, 1, GDK_ACTION_MOVE);
                 g_signal_connect (self->drag_handle, "drag-begin", G_CALLBACK (drag_begin), NULL);
+                g_signal_connect (self->drag_handle, "drag-end", G_CALLBACK (drag_end), NULL);
                 g_signal_connect (self->drag_handle, "drag-data-get", G_CALLBACK (drag_data_get), NULL);
 
-                gtk_drag_dest_set (GTK_WIDGET (self), GTK_DEST_DEFAULT_ALL, entries, 1, GDK_ACTION_MOVE);
-                g_signal_connect (self, "drag-data-received", G_CALLBACK (drag_data_received), NULL);
-
                 ingredients_model = get_ingredients_model ();
                 completion = gtk_entry_completion_new ();
                 gtk_entry_completion_set_model (completion, ingredients_model);
@@ -802,9 +790,6 @@ setup_editable_row (GrIngredientsViewerRow *self)
                 g_signal_handlers_disconnect_by_func (self->drag_handle, drag_begin, NULL);
                 g_signal_handlers_disconnect_by_func (self->drag_handle, drag_data_get, NULL);
 
-                gtk_drag_dest_unset (GTK_WIDGET (self));
-                g_signal_handlers_disconnect_by_func (self, drag_data_received, NULL);
-
                 gtk_entry_set_completion (GTK_ENTRY (self->ingredient_entry), NULL);
                 gtk_entry_set_completion (GTK_ENTRY (self->unit_entry), NULL);
         }
diff --git a/src/gr-ingredients-viewer.c b/src/gr-ingredients-viewer.c
index 32cdc92..882f101 100644
--- a/src/gr-ingredients-viewer.c
+++ b/src/gr-ingredients-viewer.c
@@ -49,6 +49,10 @@ struct _GrIngredientsViewer
         GtkSizeGroup *group;
 
         GtkWidget *active_row;
+
+        GtkWidget *drag_row;
+        GtkWidget *row_before;
+        GtkWidget *row_after;
 };
 
 
@@ -341,6 +345,184 @@ gr_ingredients_viewer_set_title (GrIngredientsViewer *viewer,
         viewer->title = g_strdup (title);
 }
 
+static GtkTargetEntry entries[] = {
+        { "GTK_LIST_BOX_ROW", GTK_TARGET_SAME_APP, 0 }
+};
+
+void
+gr_ingredients_viewer_set_drag_row (GrIngredientsViewer *viewer,
+                                    GtkWidget           *row)
+{
+        if (viewer->drag_row) {
+                gtk_style_context_remove_class (gtk_widget_get_style_context (viewer->drag_row), 
"drag-hover");
+                gtk_style_context_remove_class (gtk_widget_get_style_context (viewer->drag_row), "drag-row");
+        }
+
+        viewer->drag_row = row;
+
+        if (viewer->drag_row)
+                gtk_style_context_add_class (gtk_widget_get_style_context (viewer->drag_row), "drag-row");
+}
+
+static GtkListBoxRow *
+get_last_row (GtkListBox *list)
+{
+        int i;
+        GtkListBoxRow *row = NULL;
+
+        for (i = 0; ; i++) {
+                GtkListBoxRow *tmp = gtk_list_box_get_row_at_index (list, i);
+                if (tmp == NULL)
+                        return row;
+                row = tmp;
+        }
+        return row;
+}
+
+static GtkListBoxRow *
+get_row_before (GtkListBox    *list,
+                GtkListBoxRow *row)
+{
+        int pos = gtk_list_box_row_get_index (row);
+        return gtk_list_box_get_row_at_index (list, pos - 1);
+}
+
+static GtkListBoxRow *
+get_row_after (GtkListBox    *list,
+               GtkListBoxRow *row)
+{
+        int pos = gtk_list_box_row_get_index (row);
+        return gtk_list_box_get_row_at_index (list, pos + 1);
+}
+
+static void
+drag_data_received (GtkWidget        *widget,
+                    GdkDragContext   *context,
+                    gint              x,
+                    gint              y,
+                    GtkSelectionData *selection_data,
+                    guint             info,
+                    guint32           time,
+                    gpointer          data)
+{
+        GrIngredientsViewer *viewer = GR_INGREDIENTS_VIEWER (data);
+        GtkWidget *row_before;
+        GtkWidget *row_after;
+        GtkWidget *parent;
+        GtkWidget *row;
+        GtkWidget *source;
+        int pos;
+
+        row_before = viewer->row_before;
+        row_after = viewer->row_after;
+
+        viewer->row_before = NULL;
+        viewer->row_after = NULL;
+
+        if (row_before)
+                gtk_style_context_remove_class (gtk_widget_get_style_context (row_before), 
"drag-hover-bottom");
+        if (row_after)
+                gtk_style_context_remove_class (gtk_widget_get_style_context (row_after), "drag-hover-top");
+
+        row = (gpointer)* (gpointer*)gtk_selection_data_get_data (selection_data);
+        source = gtk_widget_get_ancestor (row, GTK_TYPE_LIST_BOX_ROW);
+
+        gtk_style_context_remove_class (gtk_widget_get_style_context (source), "drag-row");
+        gtk_style_context_remove_class (gtk_widget_get_style_context (source), "drag-hover");
+
+        if (source == row_after)
+                return;
+
+        g_object_ref (source);
+        gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (source)), source);
+
+        if (row_after) {
+                pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (row_after));
+                parent = gtk_widget_get_parent (row_after);
+        }
+        else {
+                pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (row_before)) + 1;
+                parent = gtk_widget_get_parent (row_before);
+        }
+
+        gtk_list_box_insert (GTK_LIST_BOX (parent), source, pos);
+        g_object_unref (source);
+}
+
+static gboolean
+drag_motion (GtkWidget      *widget,
+             GdkDragContext *context,
+             int             x,
+             int             y,
+             guint           time,
+             gpointer        data)
+{
+        GrIngredientsViewer *viewer = GR_INGREDIENTS_VIEWER (data);
+        GtkAllocation alloc;
+        GtkWidget *row;
+        int hover_row_y;
+        int hover_row_height;
+
+        row = GTK_WIDGET (gtk_list_box_get_row_at_y (GTK_LIST_BOX (widget), y));
+
+        if (viewer->drag_row)
+                gtk_style_context_remove_class (gtk_widget_get_style_context (viewer->drag_row), 
"drag-hover");
+
+        if (viewer->row_before)
+                gtk_style_context_remove_class (gtk_widget_get_style_context (viewer->row_before), 
"drag-hover-bottom");
+        if (viewer->row_after)
+                gtk_style_context_remove_class (gtk_widget_get_style_context (viewer->row_after), 
"drag-hover-top");
+
+        if (row) {
+                gtk_widget_get_allocation (row, &alloc);
+                hover_row_y = alloc.y;
+                hover_row_height = alloc.height;
+
+                if (y < hover_row_y + hover_row_height/2) {
+                        viewer->row_after = row;
+                        viewer->row_before = GTK_WIDGET (get_row_before (GTK_LIST_BOX (widget), 
GTK_LIST_BOX_ROW (row)));
+                }
+                else {
+                        viewer->row_before = row;
+                        viewer->row_after = GTK_WIDGET (get_row_after (GTK_LIST_BOX (widget), 
GTK_LIST_BOX_ROW (row)));
+                }
+        }
+        else {
+                viewer->row_before = GTK_WIDGET (get_last_row (GTK_LIST_BOX (widget)));
+                viewer->row_after = NULL;
+        }
+
+        if (viewer->drag_row &&
+            (viewer->drag_row == viewer->row_before ||
+             viewer->drag_row == viewer->row_after)) {
+                gtk_style_context_add_class (gtk_widget_get_style_context (viewer->drag_row), "drag-hover");
+                return FALSE;
+        }
+
+        if (viewer->row_before)
+                gtk_style_context_add_class (gtk_widget_get_style_context (viewer->row_before), 
"drag-hover-bottom");
+        if (viewer->row_after)
+                gtk_style_context_add_class (gtk_widget_get_style_context (viewer->row_after), 
"drag-hover-top");
+
+        return TRUE;
+}
+
+static void
+drag_leave (GtkWidget      *widget,
+            GdkDragContext *context,
+            guint           time,
+            gpointer        data)
+{
+        GrIngredientsViewer *viewer = GR_INGREDIENTS_VIEWER (data);
+
+        if (viewer->drag_row)
+                gtk_style_context_remove_class (gtk_widget_get_style_context (viewer->drag_row), 
"drag-hover");
+        if (viewer->row_before)
+                gtk_style_context_remove_class (gtk_widget_get_style_context (viewer->row_before), 
"drag-hover-bottom");
+        if (viewer->row_after)
+                gtk_style_context_remove_class (gtk_widget_get_style_context (viewer->row_after), 
"drag-hover-top");
+}
+
 static void
 gr_ingredients_viewer_set_editable (GrIngredientsViewer *viewer,
                                     gboolean             editable)
@@ -358,6 +540,26 @@ gr_ingredients_viewer_set_editable (GrIngredientsViewer *viewer,
                         g_object_set (row, "editable", viewer->editable, NULL);
         }
         g_list_free (children);
+
+        if (editable) {
+                gtk_style_context_add_class (gtk_widget_get_style_context (viewer->list), "editable");
+                gtk_drag_dest_set (viewer->list,
+                                   GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+                                   entries, 1, GDK_ACTION_MOVE);
+                g_signal_connect (viewer->list, "drag-data-received",
+                                  G_CALLBACK (drag_data_received), viewer);
+                g_signal_connect (viewer->list, "drag-motion",
+                                  G_CALLBACK (drag_motion), viewer);
+                g_signal_connect (viewer->list, "drag-leave",
+                                  G_CALLBACK (drag_leave), viewer);
+        }
+        else {
+                gtk_style_context_remove_class (gtk_widget_get_style_context (viewer->list), "editable");
+                gtk_drag_dest_unset (viewer->list);
+                g_signal_handlers_disconnect_by_func (viewer->list, drag_data_received, viewer);
+                g_signal_handlers_disconnect_by_func (viewer->list, drag_motion, viewer);
+                g_signal_handlers_disconnect_by_func (viewer->list, drag_leave, viewer);
+        }
 }
 
 static void
@@ -414,8 +616,6 @@ gr_ingredients_viewer_init (GrIngredientsViewer *self)
         gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
         gtk_widget_init_template (GTK_WIDGET (self));
 
-        gtk_list_box_set_header_func (GTK_LIST_BOX (self->list), all_headers, self, NULL);
-
         self->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
 
 #if defined(ENABLE_GSPELL) && defined(GSPELL_TYPE_ENTRY)
diff --git a/src/gr-ingredients-viewer.h b/src/gr-ingredients-viewer.h
index 5de7a64..0bf8117 100644
--- a/src/gr-ingredients-viewer.h
+++ b/src/gr-ingredients-viewer.h
@@ -31,7 +31,9 @@ G_DECLARE_FINAL_TYPE (GrIngredientsViewer, gr_ingredients_viewer, GR, INGREDIENT
 void set_active_row (GrIngredientsViewer *viewer,
                      GtkWidget           *row);
 
-GtkWidget *gr_ingredients_viewer_has_error (GrIngredientsViewer *viewer);
+GtkWidget *gr_ingredients_viewer_has_error    (GrIngredientsViewer *viewer);
+void       gr_ingredients_viewer_set_drag_row (GrIngredientsViewer *viewer,
+                                               GtkWidget           *row);
 
 G_END_DECLS
 
diff --git a/src/gr-ingredients-viewer.ui b/src/gr-ingredients-viewer.ui
index 748d324..e2bf36e 100644
--- a/src/gr-ingredients-viewer.ui
+++ b/src/gr-ingredients-viewer.ui
@@ -68,13 +68,6 @@
           </object>
         </child>
         <child>
-          <object class="GtkSeparator">
-            <property name="visible">1</property>
-            <property name="expand">1</property>
-            <property name="valign">center</property>
-          </object>
-        </child>
-        <child>
           <object class="GtkEventBox" id="add_row_box">
             <property name="can-focus">1</property>
             <property name="visible">1</property>
diff --git a/src/recipes-dark.css b/src/recipes-dark.css
index f308e10..3c2e050 100644
--- a/src/recipes-dark.css
+++ b/src/recipes-dark.css
@@ -25,7 +25,7 @@
   background: #868d94;
 }
 
-.during-dnd {
+row.drag-icon {
   background: #232729;
   border: 1px solid alpha(white, 0.3);
   outline-width: 0;
diff --git a/src/recipes-light.css b/src/recipes-light.css
index bad8565..ea75b6f 100644
--- a/src/recipes-light.css
+++ b/src/recipes-light.css
@@ -23,7 +23,7 @@
   background: #cbd6e1;
 }
 
-.during-dnd {
+row.drag-icon {
   background: white;
   border: 1px solid alpha(black, 0.3);
   outline-width: 0;
diff --git a/src/recipes.css b/src/recipes.css
index f8e1d35..793b299 100644
--- a/src/recipes.css
+++ b/src/recipes.css
@@ -449,3 +449,35 @@ dialog.about box.dialog-vbox {
 label.error {
   text-decoration: underline wavy red;
 }
+
+row:not(:first-child) {
+  border-top: 1px solid alpha(gray,0.5);
+  border-bottom: 1px solid transparent;
+}
+
+.editable row:not(.drag-hover-bottom):last-child {
+  border-bottom: 1px solid alpha(gray,0.5);
+}
+
+row.drag-row {
+  color: gray;
+  background: alpha(gray,0.2);
+}
+
+row.drag-row.drag-hover {
+  border-top: 1px solid #4e9a06;
+  border-bottom: 1px solid #4e9a06;
+}
+
+row.drag-hover image,
+row.drag-hover label {
+  color: #4e9a06;
+}
+
+row.drag-hover-top {
+  border-top: 1px solid #4e9a06;
+}
+
+row.drag-hover-bottom {
+  border-bottom: 1px solid #4e9a06;
+}


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