[gnome-control-center/wip/feborges/new-search-panel: 15/19] search: Make rows reordable with drag and drop



commit 053e91b8c734f048280f74bba8b56e2704e6cdfa
Author: Felipe Borges <felipeborges gnome org>
Date:   Tue Jul 2 16:02:07 2019 +0200

    search: Make rows reordable with drag and drop
    
    Fixes #589

 panels/search/cc-search-panel-row.c  | 152 ++++++++++++++++++++++++++++++++---
 panels/search/cc-search-panel-row.h  |  15 +---
 panels/search/cc-search-panel-row.ui |  10 +++
 panels/search/cc-search-panel.c      |  39 ++++++---
 4 files changed, 182 insertions(+), 34 deletions(-)
---
diff --git a/panels/search/cc-search-panel-row.c b/panels/search/cc-search-panel-row.c
index 352f5ed94..40ef86ea0 100644
--- a/panels/search/cc-search-panel-row.c
+++ b/panels/search/cc-search-panel-row.c
@@ -21,8 +21,140 @@
 
 #include "cc-search-panel-row.h"
 
+struct _CcSearchPanelRow
+{
+  GtkListBoxRow  parent_instance;
+
+  GAppInfo      *app_info;
+
+  GtkEventBox   *drag_handle;
+  GtkImage      *icon;
+  GtkLabel      *app_name;
+  GtkSwitch     *switcher;
+
+  GtkListBox    *drag_widget;
+};
+
 G_DEFINE_TYPE (CcSearchPanelRow, cc_search_panel_row, GTK_TYPE_LIST_BOX_ROW)
 
+enum
+{
+  SIGNAL_MOVE_ROW,
+  SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST] = { 0, };
+
+static void
+drag_begin_cb (CcSearchPanelRow *self,
+               GdkDragContext   *drag_context)
+{
+  CcSearchPanelRow *drag_row;
+  GtkAllocation alloc;
+  gint x = 0, y = 0;
+
+  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+  gdk_window_get_device_position (gtk_widget_get_window (GTK_WIDGET (self)),
+                                  gdk_drag_context_get_device (drag_context),
+                                  &x, &y, NULL);
+
+  self->drag_widget = GTK_LIST_BOX (gtk_list_box_new ());
+  gtk_widget_show (GTK_WIDGET (self->drag_widget));
+  gtk_widget_set_size_request (GTK_WIDGET (self->drag_widget), alloc.width, alloc.height);
+
+  drag_row = cc_search_panel_row_new (self->app_info);
+  gtk_widget_show (GTK_WIDGET (drag_row));
+  gtk_container_add (GTK_CONTAINER (self->drag_widget), GTK_WIDGET (drag_row));
+  gtk_list_box_drag_highlight_row (self->drag_widget, GTK_LIST_BOX_ROW (drag_row));
+
+  gtk_drag_set_icon_widget (drag_context, GTK_WIDGET (self->drag_widget), x - alloc.x, y - alloc.y);
+}
+
+static void
+drag_end_cb (CcSearchPanelRow *self)
+{
+  g_clear_pointer ((GtkWidget **) &self->drag_widget, gtk_widget_destroy);
+}
+
+static void
+drag_data_get_cb (CcSearchPanelRow *self,
+                  GdkDragContext   *context,
+                  GtkSelectionData *selection_data,
+                  guint             info,
+                  guint             time_)
+{
+  gtk_selection_data_set (selection_data,
+                          gdk_atom_intern_static_string ("GTK_LIST_BOX_ROW"),
+                          32,
+                          (const guchar *)&self,
+                          sizeof (gpointer));
+}
+
+static void
+drag_data_received_cb (CcSearchPanelRow *self,
+                       GdkDragContext   *context,
+                       gint              x,
+                       gint              y,
+                       GtkSelectionData *selection_data,
+                       guint             info,
+                       guint             time_)
+{
+  CcSearchPanelRow *source;
+
+  source = *((CcSearchPanelRow **) gtk_selection_data_get_data (selection_data));
+  if (source == self)
+    return;
+
+  g_signal_emit (source,
+                 signals[SIGNAL_MOVE_ROW],
+                 0,
+                 self);
+}
+
+static GtkTargetEntry entries[] =
+{
+  { "GTK_LIST_BOX_ROW", GTK_TARGET_SAME_APP, 0 }
+};
+
+static void
+cc_search_panel_row_class_init (CcSearchPanelRowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/control-center/search/cc-search-panel-row.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, CcSearchPanelRow, drag_handle);
+  gtk_widget_class_bind_template_child (widget_class, CcSearchPanelRow, icon);
+  gtk_widget_class_bind_template_child (widget_class, CcSearchPanelRow, app_name);
+  gtk_widget_class_bind_template_child (widget_class, CcSearchPanelRow, switcher);
+
+  gtk_widget_class_bind_template_callback (widget_class, drag_begin_cb);
+  gtk_widget_class_bind_template_callback (widget_class, drag_end_cb);
+  gtk_widget_class_bind_template_callback (widget_class, drag_data_get_cb);
+  gtk_widget_class_bind_template_callback (widget_class, drag_data_received_cb);
+
+  signals[SIGNAL_MOVE_ROW] =
+    g_signal_new ("move-row",
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL,
+                  NULL,
+                  G_TYPE_NONE,
+                  1, CC_TYPE_SEARCH_PANEL_ROW);
+}
+
+static void
+cc_search_panel_row_init (CcSearchPanelRow *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gtk_drag_source_set (GTK_WIDGET (self->drag_handle), GDK_BUTTON1_MASK, entries, 1, GDK_ACTION_MOVE);
+  gtk_drag_dest_set (GTK_WIDGET (self), GTK_DEST_DEFAULT_ALL, entries, 1, GDK_ACTION_MOVE);
+}
+
 CcSearchPanelRow *
 cc_search_panel_row_new (GAppInfo *app_info)
 {
@@ -44,23 +176,19 @@ cc_search_panel_row_new (GAppInfo *app_info)
 
   gtk_label_set_text (self->app_name, g_app_info_get_name (app_info));
 
+  gtk_drag_source_set (GTK_WIDGET (self->drag_handle), GDK_BUTTON1_MASK, entries, 1, GDK_ACTION_MOVE);
+
   return self;
 }
 
-static void
-cc_search_panel_row_class_init (CcSearchPanelRowClass *klass)
+GAppInfo *
+cc_search_panel_row_get_app_info (CcSearchPanelRow *row)
 {
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
-  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/control-center/search/cc-search-panel-row.ui");
-
-  gtk_widget_class_bind_template_child (widget_class, CcSearchPanelRow, icon);
-  gtk_widget_class_bind_template_child (widget_class, CcSearchPanelRow, app_name);
-  gtk_widget_class_bind_template_child (widget_class, CcSearchPanelRow, switcher);
+    return row->app_info;
 }
 
-static void
-cc_search_panel_row_init (CcSearchPanelRow *self)
+GtkWidget *
+cc_search_panel_row_get_switch (CcSearchPanelRow *row)
 {
-  gtk_widget_init_template (GTK_WIDGET (self));
+    return GTK_WIDGET (row->switcher);
 }
diff --git a/panels/search/cc-search-panel-row.h b/panels/search/cc-search-panel-row.h
index 9f14e2d30..d8a952661 100644
--- a/panels/search/cc-search-panel-row.h
+++ b/panels/search/cc-search-panel-row.h
@@ -24,21 +24,12 @@
 G_BEGIN_DECLS
 
 #define CC_TYPE_SEARCH_PANEL_ROW (cc_search_panel_row_get_type())
-
 G_DECLARE_FINAL_TYPE (CcSearchPanelRow, cc_search_panel_row, CC, SEARCH_PANEL_ROW, GtkListBoxRow)
 
-struct _CcSearchPanelRow
-{
-  GtkListBoxRow  parent_instance;
-
-  GAppInfo      *app_info;
-
-  GtkImage      *icon;
-  GtkLabel      *app_name;
-  GtkSwitch     *switcher;
-};
+CcSearchPanelRow *cc_search_panel_row_new          (GAppInfo         *app_info);
 
+GAppInfo         *cc_search_panel_row_get_app_info (CcSearchPanelRow *row);
 
-CcSearchPanelRow *cc_search_panel_row_new (GAppInfo *app_info);
+GtkWidget        *cc_search_panel_row_get_switch   (CcSearchPanelRow *row);
 
 G_END_DECLS
diff --git a/panels/search/cc-search-panel-row.ui b/panels/search/cc-search-panel-row.ui
index 2c82eeffd..571d6507c 100644
--- a/panels/search/cc-search-panel-row.ui
+++ b/panels/search/cc-search-panel-row.ui
@@ -4,14 +4,24 @@
   <template class="CcSearchPanelRow" parent="GtkListBoxRow">
     <property name="visible">True</property>
     <property name="can-focus">True</property>
+    <signal name="drag_data_received" handler="drag_data_received_cb" object="CcSearchPanelRow" 
swapped="yes"/>
     <child>
       <object class="GtkEventBox" id="drag_handle">
         <property name="visible">True</property>
+        <signal name="drag_data_get" handler="drag_data_get_cb" object="CcSearchPanelRow" swapped="yes"/>
+        <signal name="drag-begin" handler="drag_begin_cb" object="CcSearchPanelRow" swapped="yes"/>
+        <signal name="drag_end" handler="drag_end_cb" object="CcSearchPanelRow" swapped="yes"/>
         <child>
           <object class="GtkBox">
             <property name="visible">True</property>
             <property name="spacing">10</property>
             <property name="border-width">10</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="icon-name">open-menu-symbolic</property>
+              </object>
+            </child>
             <child>
               <object class="GtkImage" id="icon">
                 <property name="visible">True</property>
diff --git a/panels/search/cc-search-panel.c b/panels/search/cc-search-panel.c
index 0e20b9073..f4765556b 100644
--- a/panels/search/cc-search-panel.c
+++ b/panels/search/cc-search-panel.c
@@ -60,8 +60,8 @@ list_sort_func (gconstpointer a,
   gint idx_a, idx_b;
   gpointer lookup;
 
-  app_a = CC_SEARCH_PANEL_ROW ((gpointer*)a)->app_info;
-  app_b = CC_SEARCH_PANEL_ROW ((gpointer*)b)->app_info;
+  app_a = cc_search_panel_row_get_app_info (CC_SEARCH_PANEL_ROW ((gpointer*)a));
+  app_b = cc_search_panel_row_get_app_info (CC_SEARCH_PANEL_ROW ((gpointer*)b));
 
   id_a = g_app_info_get_id (app_a);
   id_b = g_app_info_get_id (app_b);
@@ -194,7 +194,7 @@ search_panel_move_selected (CcSearchPanel *self,
   GList *l, *other;
 
   row = gtk_list_box_get_selected_row (GTK_LIST_BOX (self->list_box));
-  app_info = CC_SEARCH_PANEL_ROW (row)->app_info;
+  app_info = cc_search_panel_row_get_app_info (CC_SEARCH_PANEL_ROW (row));
   app_id = g_app_info_get_id (app_info);
 
   children = gtk_container_get_children (GTK_CONTAINER (self->list_box));
@@ -209,7 +209,7 @@ search_panel_move_selected (CcSearchPanel *self,
   g_assert (other != NULL);
 
   other_row = other->data;
-  other_app_info = CC_SEARCH_PANEL_ROW (other_row)->app_info;
+  other_app_info = cc_search_panel_row_get_app_info (CC_SEARCH_PANEL_ROW (other_row));
   other_app_id = g_app_info_get_id (other_app_info);
 
   g_assert (other_app_id != NULL);
@@ -235,7 +235,7 @@ search_panel_move_selected (CcSearchPanel *self,
           break;
         }
 
-      tmp = CC_SEARCH_PANEL_ROW (l->data)->app_info;
+      tmp = cc_search_panel_row_get_app_info (CC_SEARCH_PANEL_ROW (l->data));
       tmp_id = g_app_info_get_id (tmp);
 
       last_good_app = tmp_id;
@@ -268,7 +268,7 @@ search_panel_move_selected (CcSearchPanel *self,
       GAppInfo *tmp;
       const char *tmp_id;
 
-      tmp = CC_SEARCH_PANEL_ROW (l->data)->app_info;
+      tmp = cc_search_panel_row_get_app_info (CC_SEARCH_PANEL_ROW (l->data));
       tmp_id = g_app_info_get_id (tmp);
 
       g_hash_table_replace (self->sort_order, g_strdup (tmp_id), GINT_TO_POINTER (idx));
@@ -287,6 +287,22 @@ search_panel_move_selected (CcSearchPanel *self,
   search_panel_propagate_sort_order (self);
 }
 
+static void
+row_moved_cb (CcSearchPanel    *self,
+              CcSearchPanelRow *dest_row,
+              CcSearchPanelRow *row)
+{
+  gint source_idx = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (row));
+  gint dest_idx = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (dest_row));
+  gboolean down;
+
+  gtk_list_box_select_row (GTK_LIST_BOX (self->list_box), GTK_LIST_BOX_ROW (row));
+
+  down = (source_idx - dest_idx) < 0;
+  for (int i = 0; i < ABS (source_idx - dest_idx); i++)
+    search_panel_move_selected (self, down);
+}
+
 static void
 down_button_clicked (GtkWidget *widget,
                      CcSearchPanel *self)
@@ -324,7 +340,7 @@ switch_settings_mapping_set_generic (const GValue *value,
                                      gboolean default_enabled)
 {
   CcSearchPanel *self = g_object_get_data (G_OBJECT (row), "self");
-  GAppInfo *app_info = CC_SEARCH_PANEL_ROW (row)->app_info;
+  GAppInfo *app_info = cc_search_panel_row_get_app_info (CC_SEARCH_PANEL_ROW (row));
   g_auto(GStrv) apps = NULL;
   g_autoptr(GPtrArray) new_apps = NULL;
   gint idx;
@@ -381,7 +397,7 @@ switch_settings_mapping_get_generic (GValue *value,
                                      GtkWidget *row,
                                      gboolean default_enabled)
 {
-  GAppInfo *app_info = CC_SEARCH_PANEL_ROW (row)->app_info;
+  GAppInfo *app_info = cc_search_panel_row_get_app_info (CC_SEARCH_PANEL_ROW (row));
   g_autofree const gchar **apps = NULL;
   gint idx;
   gboolean found;
@@ -439,13 +455,16 @@ search_panel_add_one_app_info (CcSearchPanel *self,
   gtk_widget_set_valign (self->list_box, GTK_ALIGN_FILL);
 
   row = cc_search_panel_row_new (app_info);
+  g_signal_connect_object (row, "move-row",
+                           G_CALLBACK (row_moved_cb), self,
+                           G_CONNECT_SWAPPED);
   g_object_set_data (G_OBJECT (row), "self", self);
   gtk_container_add (GTK_CONTAINER (self->list_box), GTK_WIDGET (row));
 
   if (default_enabled)
     {
       g_settings_bind_with_mapping (self->search_settings, "disabled",
-                                    row->switcher, "active",
+                                    cc_search_panel_row_get_switch (row), "active",
                                     G_SETTINGS_BIND_DEFAULT,
                                     switch_settings_mapping_get_default_enabled,
                                     switch_settings_mapping_set_default_enabled,
@@ -454,7 +473,7 @@ search_panel_add_one_app_info (CcSearchPanel *self,
   else
     {
       g_settings_bind_with_mapping (self->search_settings, "enabled",
-                                    row->switcher, "active",
+                                    cc_search_panel_row_get_switch (row), "active",
                                     G_SETTINGS_BIND_DEFAULT,
                                     switch_settings_mapping_get_default_disabled,
                                     switch_settings_mapping_set_default_disabled,


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