[gnome-control-center/wip/feborges/new-search-panel: 4/4] search: FIXME commit message: introduce Drag and Drop



commit 8093a856d9039bd6fcc5019197de216c49e29012
Author: Felipe Borges <felipeborges gnome org>
Date:   Wed Oct 26 14:31:32 2016 +0200

    search: FIXME commit message: introduce Drag and Drop

 panels/search/Makefile.am       |    2 +
 panels/search/cc-search-panel.c |   43 ++++-
 panels/search/dndlistbox.c      |  401 +++++++++++++++++++++++++++++++++++++++
 panels/search/dndlistbox.h      |   22 ++
 panels/search/search.ui         |    2 +-
 5 files changed, 463 insertions(+), 7 deletions(-)
---
diff --git a/panels/search/Makefile.am b/panels/search/Makefile.am
index 2041bc0..9971191 100644
--- a/panels/search/Makefile.am
+++ b/panels/search/Makefile.am
@@ -18,6 +18,8 @@ libsearch_la_SOURCES =                \
        $(BUILT_SOURCES)        \
        cc-search-locations-dialog.c \
        cc-search-locations-dialog.h \
+       dndlistbox.h            \
+       dndlistbox.c            \
        cc-search-panel.c       \
        cc-search-panel.h
 
diff --git a/panels/search/cc-search-panel.c b/panels/search/cc-search-panel.c
index 35a4586..726cb8c 100644
--- a/panels/search/cc-search-panel.c
+++ b/panels/search/cc-search-panel.c
@@ -21,6 +21,7 @@
 #include "cc-search-panel.h"
 #include "cc-search-locations-dialog.h"
 #include "cc-search-resources.h"
+#include "dndlistbox.h"
 #include "shell/list-box-helper.h"
 
 #include <gio/gdesktopappinfo.h>
@@ -183,7 +184,7 @@ search_panel_set_no_providers (CcSearchPanel *self)
 
 static void
 search_panel_move_selected (CcSearchPanel *self,
-                            gboolean down)
+                            gint           position)
 {
   GtkListBoxRow *row, *other_row;
   GAppInfo *app_info, *other_app_info;
@@ -206,7 +207,7 @@ search_panel_move_selected (CcSearchPanel *self,
   l = g_list_find (children, row);
   g_assert (l != NULL);
 
-  other = down ? g_list_next(l) : g_list_previous(l);
+  other = g_list_nth (l, position);
   g_assert (other != NULL);
 
   other_row = other->data;
@@ -280,7 +281,7 @@ search_panel_move_selected (CcSearchPanel *self,
     }
 
   other_idx = GPOINTER_TO_INT (g_hash_table_lookup (self->priv->sort_order, app_id));
-  idx = down ? (other_idx + 1) : (other_idx - 1);
+  idx = position;
 
   g_hash_table_replace (self->priv->sort_order, g_strdup (other_app_id), GINT_TO_POINTER (other_idx));
   g_hash_table_replace (self->priv->sort_order, g_strdup (app_id), GINT_TO_POINTER (idx));
@@ -429,12 +430,35 @@ switch_settings_mapping_get_default_disabled (GValue *value,
                                               user_data, FALSE);
 }
 
+static gboolean
+row_on_button_pressed (GtkWidget            *row,
+                       GdkEventButton       *event,
+                       CcSearchPanel        *self)
+{
+  if (gdk_event_get_event_type ((GdkEvent *)event) == GDK_BUTTON_PRESS)
+    {
+      DndListBox *box = DND_LIST_BOX (self->priv->list_box);
+
+      if (event->button == GDK_BUTTON_PRIMARY)
+        {
+          dnd_list_box_set_drag_row (box, row, event);
+
+          return FALSE;
+        }
+
+      dnd_list_box_set_drag_row (box, NULL, NULL);
+    }
+
+  return FALSE;
+}
+
 static void
 search_panel_add_one_app_info (CcSearchPanel *self,
                                GAppInfo *app_info,
                                gboolean default_enabled)
 {
   GtkWidget *row, *box, *w;
+  GtkWidget *event_box;
   GIcon *icon;
   gint width, height;
 
@@ -447,9 +471,11 @@ search_panel_add_one_app_info (CcSearchPanel *self,
   /* reset valignment of the list box */
   gtk_widget_set_valign (self->priv->list_box, GTK_ALIGN_FILL);
 
-  row = gtk_list_box_row_new ();
+  event_box = gtk_event_box_new ();
   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
-  gtk_container_add (GTK_CONTAINER (row), box);
+  gtk_container_add (GTK_CONTAINER (event_box), box);
+  row = gtk_list_box_row_new ();
+  gtk_container_add (GTK_CONTAINER (row), event_box);
   gtk_widget_set_hexpand (box, TRUE);
   gtk_container_set_border_width (GTK_CONTAINER (box), 6);
   g_object_set_data_full (G_OBJECT (row), "app-info",
@@ -463,6 +489,9 @@ search_panel_add_one_app_info (CcSearchPanel *self,
   else
     g_object_ref (icon);
 
+  w = gtk_image_new_from_icon_name ("open-menu-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
+  gtk_container_add (GTK_CONTAINER (box), w);
+
   w = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
   gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &width, &height);
   gtk_image_set_pixel_size (GTK_IMAGE (w), MAX (width, height));
@@ -495,6 +524,8 @@ search_panel_add_one_app_info (CcSearchPanel *self,
                                     row, NULL);
     }
 
+  g_signal_connect (row, "button-press-event", G_CALLBACK (row_on_button_pressed), self);
+
   gtk_widget_show_all (row);
 }
 
@@ -764,7 +795,7 @@ cc_search_panel_init (CcSearchPanel *self)
     }
 
   frame = WID ("search_frame");
-  widget = GTK_WIDGET (gtk_list_box_new ());
+  widget = GTK_WIDGET (dnd_list_box_new ());
   gtk_list_box_set_sort_func (GTK_LIST_BOX (widget),
                               (GtkListBoxSortFunc)list_sort_func, self, NULL);
   gtk_list_box_set_header_func (GTK_LIST_BOX (widget), cc_list_box_update_header_func, NULL, NULL);
diff --git a/panels/search/dndlistbox.c b/panels/search/dndlistbox.c
new file mode 100644
index 0000000..92be835
--- /dev/null
+++ b/panels/search/dndlistbox.c
@@ -0,0 +1,401 @@
+#include "dndlistbox.h"
+
+struct _DndListBox {
+  GtkListBox parent;
+
+  GtkTargetList *source_targets;
+  GtkWidget *dnd_window;
+  GtkWidget *row_placeholder;
+  gint row_placeholder_index;
+  gint row_destination_index;
+  GtkWidget *drag_row;
+  gint row_source_row_offset;
+  gint drag_row_height;
+  gint drag_row_x;
+  gint drag_row_y;
+  gint drag_root_x;
+  gint drag_root_y;
+  gboolean is_on_drag;
+};
+
+G_DEFINE_TYPE (DndListBox, dnd_list_box, GTK_TYPE_LIST_BOX)
+
+#define ROW_OUTSIDE_LISTBOX -1
+
+GtkWidget *
+dnd_list_box_new (void)
+{
+  return g_object_new (DND_LIST_BOX_TYPE_, NULL);
+}
+
+void
+dnd_list_box_set_drag_row (DndListBox     *self,
+                           GtkWidget      *row,
+                           GdkEventButton *event)
+{
+
+  self->drag_row = row;
+
+  if (event != NULL)
+    {
+      self->drag_row_x = (gint)event->x;
+      self->drag_row_y = (gint)event->y;
+      self->drag_root_x = event->x_root;
+      self->drag_root_y = event->y_root;
+    }
+}
+
+static void
+dnd_list_box_drag_data_received (GtkWidget        *widget,
+                                 GdkDragContext   *context,
+                                 gint              x,
+                                 gint              y,
+                                 GtkSelectionData *data,
+                                 guint             info,
+                                 guint             time)
+{
+  DndListBox *self = DND_LIST_BOX (widget);
+  GtkWidget *source = NULL;
+  GtkWidget **source_row;
+
+  source = gtk_drag_get_source_widget (context);
+  if (source)
+    gtk_drag_finish (context, TRUE, FALSE, time);
+
+  source_row = (void*)gtk_selection_data_get_data (data);
+  if (source && gtk_selection_data_get_target (data) == gdk_atom_intern_static_string ("GTK_LIST_BOX_ROW"))
+    {
+      gint source_index = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (*source_row));
+
+      /* move the row */
+      if (DND_LIST_BOX (source) != self ||
+          (self->row_destination_index != source_index &&
+           self->row_destination_index != source_index + 1))
+        {
+          GtkListBoxRow *old_row, *new_row, *child;
+          gint position;
+
+          position = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (*source_row));
+
+          old_row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (self), self->row_destination_index);
+
+         g_print ("from = %d to %d\n", position, self->row_destination_index);
+        }
+    }
+}
+
+static void
+dnd_list_box_drag_data_get (GtkWidget        *widget,
+                            GdkDragContext   *context,
+                            GtkSelectionData *data,
+                            guint             info,
+                            guint             time)
+{
+  DndListBox *self = DND_LIST_BOX (widget);
+  GdkAtom target, result;
+
+  target = gtk_selection_data_get_target (data);
+  if (target == gdk_atom_intern_static_string ("GTK_LIST_BOX_ROW"))
+    {
+      gtk_selection_data_set (data, target, 8, (void*)&self->drag_row, sizeof (gpointer));
+
+      return;
+    }
+
+  result = gtk_drag_dest_find_target (widget, context, self->source_targets);
+  if (result != GDK_NONE)
+    g_print ("result != GDK_NONE\n"); /* FIXME */
+
+  gtk_widget_show (self->drag_row);
+}
+
+static gboolean
+dnd_list_box_drag_drop (GtkWidget      *widget,
+                        GdkDragContext *context,
+                        gint            x,
+                        gint            y,
+                        guint           time)
+{
+  DndListBox *self = DND_LIST_BOX (widget);
+  GtkWidget *source_widget;
+  GdkAtom target;
+
+  target = gtk_drag_dest_find_target (widget, context, NULL);
+  source_widget = gtk_drag_get_source_widget (context);
+
+  if (DND_IS_LIST_BOX (source_widget))
+    gtk_widget_show (self->drag_row);
+
+  if (target == gdk_atom_intern_static_string ("GTK_LIST_BOX_ROW"))
+    {
+      gtk_drag_get_data (widget, context, target, time);
+      return TRUE;
+    }
+
+  self->row_placeholder_index = ROW_OUTSIDE_LISTBOX;
+
+  return FALSE;
+}
+
+static void
+dnd_list_box_drag_leave (GtkWidget      *widget,
+                         GdkDragContext *context,
+                         guint           time)
+{
+  DndListBox *self = DND_LIST_BOX (widget);
+
+  if (self->row_placeholder_index != ROW_OUTSIDE_LISTBOX)
+    {
+      gtk_container_remove (GTK_CONTAINER (self), self->row_placeholder);
+      self->row_placeholder_index = ROW_OUTSIDE_LISTBOX;
+    }
+
+  gtk_list_box_drag_unhighlight_row (GTK_LIST_BOX (widget));
+}
+
+static GtkWidget *
+create_placeholder_row (gint height)
+{
+  GtkWidget *row;
+
+  row = gtk_list_box_row_new ();
+  gtk_widget_set_size_request (row, -1, height);
+
+  return row;
+}
+
+static gboolean
+dnd_list_box_drag_motion (GtkWidget      *widget,
+                          GdkDragContext *context,
+                          gint            x,
+                          gint            y,
+                          gint            time)
+{
+  DndListBox *self = DND_LIST_BOX (widget);
+  GtkListBoxRow *generic_row;
+  GtkWidget *source;
+  GdkAtom target;
+  gint dest_x, dest_y;
+  gint row_placeholder_index;
+  gint row_index;
+
+  target = gtk_drag_dest_find_target (widget, context, NULL);
+  if (target != gdk_atom_intern_static_string ("GTK_LIST_BOX_ROW"))
+    {
+      gdk_drag_status (context, 0, time);
+      return FALSE;
+    }
+
+  gtk_widget_translate_coordinates (widget, self, x, y, &dest_x, &dest_y);
+
+  generic_row = (GtkListBoxRow *)gtk_list_box_get_row_at_y (GTK_LIST_BOX (self), dest_y);
+  source = gtk_drag_get_source_widget (context);
+
+  if (!self->row_placeholder)
+    {
+      if (!generic_row)
+        {
+          self->drag_row_height = DND_LIST_BOX (source)->drag_row_height;
+        }
+      else
+        {
+          GtkAllocation allocation;
+
+          gtk_widget_get_allocation (GTK_WIDGET (generic_row), &allocation);
+          self->drag_row_height = allocation.height;
+        }
+
+      self->row_placeholder = create_placeholder_row (self->drag_row_height);
+      gtk_widget_show (self->row_placeholder);
+      g_object_ref_sink (self->row_placeholder);
+    }
+  else if (GTK_WIDGET (generic_row) == self->row_placeholder)
+    {
+      gdk_drag_status (context, GDK_ACTION_MOVE, time);
+
+      return TRUE;
+    }
+
+  if (!generic_row)
+    {
+      GList *children;
+
+      children = gtk_container_get_children (GTK_CONTAINER (self));
+      row_placeholder_index = g_list_length (children);
+
+      g_list_free (children);
+    }
+  else
+    {
+      row_index = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (generic_row));
+
+      gtk_widget_translate_coordinates (widget, GTK_WIDGET (generic_row), x, y, &dest_x, &dest_y);
+
+      if (dest_y <= self->drag_row_height / 2 && row_index > 0)
+        {
+          row_placeholder_index = row_index;
+        }
+      else
+        {
+          row_placeholder_index = row_index + 1;
+        }
+    }
+
+  if (source == widget)
+    {
+      gint source_row_index;
+
+      source_row_index = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (self->drag_row));
+      self->row_source_row_offset = source_row_index < row_placeholder_index ? -1 : 0;
+    }
+
+  if (self->row_placeholder_index != row_placeholder_index)
+    {
+      if (self->row_placeholder_index != ROW_OUTSIDE_LISTBOX)
+        {
+          gtk_container_remove (GTK_CONTAINER (self), self->row_placeholder);
+
+          if (self->row_placeholder_index < row_placeholder_index)
+            row_placeholder_index -= -1; // FIXME: really?
+       }
+
+      self->row_destination_index = self->row_placeholder_index = row_placeholder_index;
+
+      gtk_list_box_insert (GTK_LIST_BOX (self), self->row_placeholder, self->row_placeholder_index);
+    }
+
+  gdk_drag_status (context, GDK_ACTION_MOVE, time);
+
+  return TRUE;
+}
+
+static gboolean
+dnd_list_box_drag_failed (GtkWidget      *widget,
+                          GdkDragContext *context,
+                          GtkDragResult   result)
+{
+  GtkWidget *source;
+
+  source = gtk_drag_get_source_widget (context);
+  if (DND_IS_LIST_BOX (source))
+    gtk_widget_show (DND_LIST_BOX (widget)->drag_row);
+
+  return FALSE;
+}
+
+static void
+dnd_list_box_drag_end (GtkWidget      *widget,
+                       GdkDragContext *context)
+{
+  DndListBox *self = DND_LIST_BOX (widget);
+
+  self->drag_row = NULL;
+  self->is_on_drag = FALSE;
+
+  gtk_widget_destroy (self->dnd_window);
+  self->dnd_window = NULL;
+}
+
+static void
+dnd_list_box_drag_begin (GtkWidget      *widget,
+                         GdkDragContext *context)
+{
+  DndListBox *self = DND_LIST_BOX (widget);
+  GtkWidget *drag_row, *row;
+  GtkAllocation allocation;
+
+  drag_row = self->drag_row;
+  gtk_widget_get_allocation (drag_row, &allocation);
+  gtk_widget_hide (drag_row);
+
+  self->drag_row_height = allocation.height;
+
+  /* populate the row */
+  row = gtk_list_box_row_new ();
+
+  self->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
+  gtk_widget_set_size_request (self->dnd_window, allocation.width, allocation.height);
+  gtk_window_set_screen (GTK_WINDOW (self->dnd_window), gtk_widget_get_screen (drag_row));
+
+  gtk_container_add (GTK_CONTAINER (self->dnd_window), row);
+  gtk_widget_show_all (self->dnd_window);
+  gtk_widget_set_opacity (self->dnd_window, 0.8);
+
+  gtk_drag_set_icon_widget (context, self->dnd_window, self->drag_row_x, self->drag_row_y);
+}
+
+static gboolean
+dnd_list_box_motion_notify_event (GtkWidget      *widget,
+                                  GdkEventMotion *event)
+{
+  DndListBox *self = DND_LIST_BOX (widget);
+
+  if (self->drag_row == NULL || self->is_on_drag)
+    return FALSE;
+
+  if (!(event->state & GDK_BUTTON1_MASK))
+    {
+      self->drag_row = NULL;
+
+      return FALSE;
+    }
+
+  if (gtk_drag_check_threshold (widget, self->drag_root_x, self->drag_root_y, event->x_root, event->y_root))
+    {
+      self->is_on_drag = TRUE;
+
+      gtk_drag_begin_with_coordinates (widget, self->source_targets, GDK_ACTION_MOVE, GDK_BUTTON_PRIMARY, 
(GdkEvent*)event, -1, -1);
+    }
+
+  return FALSE;
+}
+
+static void
+dnd_list_box_finalize (GObject *object)
+{
+  DndListBox *self = DND_LIST_BOX (object);
+
+  G_OBJECT_CLASS (dnd_list_box_parent_class)->finalize (object);
+}
+
+static void
+dnd_list_box_class_init (DndListBoxClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = dnd_list_box_finalize;
+
+  widget_class->motion_notify_event = dnd_list_box_motion_notify_event;
+  widget_class->drag_begin = dnd_list_box_drag_begin;
+  widget_class->drag_end = dnd_list_box_drag_end;
+  widget_class->drag_failed = dnd_list_box_drag_failed;
+  widget_class->drag_motion = dnd_list_box_drag_motion;
+  widget_class->drag_leave = dnd_list_box_drag_leave;
+  widget_class->drag_drop = dnd_list_box_drag_drop;
+  widget_class->drag_data_get = dnd_list_box_drag_data_get;
+  widget_class->drag_data_received = dnd_list_box_drag_data_received;
+}
+
+static const GtkTargetEntry targets [] = {
+  { "GTK_LIST_BOX_ROW", GTK_TARGET_SAME_APP, 0 },
+};
+
+static void
+dnd_list_box_init (DndListBox *self)
+{
+  self->source_targets = gtk_target_list_new (targets, G_N_ELEMENTS (targets));
+  gtk_target_list_add_text_targets (self->source_targets, 0);
+
+  gtk_drag_dest_set (GTK_WIDGET (self), 0, targets, G_N_ELEMENTS (targets), GDK_ACTION_MOVE);
+
+  gtk_drag_dest_set_track_motion (GTK_WIDGET (self), TRUE);
+
+  self->drag_row = NULL;
+  self->row_placeholder = NULL;
+  self->row_placeholder_index = ROW_OUTSIDE_LISTBOX;
+  self->row_destination_index = ROW_OUTSIDE_LISTBOX;
+  self->row_source_row_offset = 0;
+  self->is_on_drag = FALSE;
+}
+
diff --git a/panels/search/dndlistbox.h b/panels/search/dndlistbox.h
new file mode 100644
index 0000000..2c53d13
--- /dev/null
+++ b/panels/search/dndlistbox.h
@@ -0,0 +1,22 @@
+#ifndef DND_LIST_BOX_H
+#define DND_LIST_BOX_H
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define DND_LIST_BOX_TYPE_ (dnd_list_box_get_type())
+
+G_DECLARE_FINAL_TYPE (DndListBox, dnd_list_box, DND, LIST_BOX, GtkListBox)
+
+GtkWidget *dnd_list_box_new          (void);
+
+void       dnd_list_box_set_drag_row (DndListBox     *self,
+                                      GtkWidget      *row,
+                                      GdkEventButton *event);
+
+G_END_DECLS
+
+#endif /* DND_LIST_BOX_H */
+
diff --git a/panels/search/search.ui b/panels/search/search.ui
index 4d4c067..2b4ae03 100644
--- a/panels/search/search.ui
+++ b/panels/search/search.ui
@@ -4,7 +4,7 @@
   <object class="GtkScrolledWindow" id="search_vbox">
     <property name="visible">True</property>
     <property name="shadow-type">in</property>
-
+    <property name="height-request">490</property>
     <child>
       <object class="GtkBox">
         <property name="visible">True</property>


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