[gnome-control-center/wip/feborges/new-search-panel: 1/2] search: Introduce Drag and Drop between rows
- From: Felipe Borges <felipeborges src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center/wip/feborges/new-search-panel: 1/2] search: Introduce Drag and Drop between rows
- Date: Thu, 3 Nov 2016 13:49:22 +0000 (UTC)
commit 1aa8613f2ddc68685a19a35fcb604a2c388ca198
Author: Felipe Borges <felipeborges gnome org>
Date: Wed Oct 26 14:31:32 2016 +0200
search: Introduce Drag and Drop between rows
The DndListBox is a GtkListBox which supports Drag and Drop. It
emits "row-moved" with the dragged row.
These changes are according to the design guidelines available
at https://wiki.gnome.org/Design/SystemSettings/Search
panels/search/Makefile.am | 2 +
panels/search/cc-search-panel.c | 140 ++++++++++--
panels/search/dndlistbox.c | 474 +++++++++++++++++++++++++++++++++++++++
panels/search/dndlistbox.h | 57 +++++
panels/search/search.ui | 2 +-
5 files changed, 649 insertions(+), 26 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..919be11 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>
@@ -429,33 +430,42 @@ switch_settings_mapping_get_default_disabled (GValue *value,
user_data, FALSE);
}
-static void
-search_panel_add_one_app_info (CcSearchPanel *self,
- GAppInfo *app_info,
- gboolean default_enabled)
+static gboolean
+row_on_button_pressed (GtkWidget *row,
+ GdkEventButton *event,
+ CcSearchPanel *self)
{
- GtkWidget *row, *box, *w;
- GIcon *icon;
- gint width, height;
+ /* unset sort function to allow moving rows with drag and drop. */
+ gtk_list_box_set_sort_func (GTK_LIST_BOX (self->priv->list_box), NULL, NULL, NULL);
- /* gnome-control-center is special cased in the shell,
- and is not configurable */
- if (g_strcmp0 (g_app_info_get_id (app_info),
- "gnome-control-center.desktop") == 0)
- return;
+ if (gdk_event_get_event_type ((GdkEvent *)event) == GDK_BUTTON_PRESS)
+ {
+ DndListBox *box = DND_LIST_BOX (self->priv->list_box);
- /* reset valignment of the list box */
- gtk_widget_set_valign (self->priv->list_box, GTK_ALIGN_FILL);
+ 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 GtkWidget *
+create_row (GAppInfo *app_info,
+ GtkWidget *switcher)
+{
+ GtkWidget *row, *event_box, *box, *w;
+ GIcon *icon;
+ gint width, height;
- row = gtk_list_box_row_new ();
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
- gtk_container_add (GTK_CONTAINER (row), 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",
- g_object_ref (app_info), g_object_unref);
- g_object_set_data (G_OBJECT (row), "self", self);
- gtk_container_add (GTK_CONTAINER (self->priv->list_box), row);
icon = g_app_info_get_icon (app_info);
if (icon == NULL)
@@ -463,18 +473,52 @@ search_panel_add_one_app_info (CcSearchPanel *self,
else
g_object_ref (icon);
- w = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
- gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &width, &height);
+ w = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DND);
+ gtk_icon_size_lookup (GTK_ICON_SIZE_DND, &width, &height);
gtk_image_set_pixel_size (GTK_IMAGE (w), MAX (width, height));
+ gtk_widget_set_margin_start (w, 10);
gtk_container_add (GTK_CONTAINER (box), w);
g_object_unref (icon);
w = gtk_label_new (g_app_info_get_name (app_info));
gtk_container_add (GTK_CONTAINER (box), w);
+ if (switcher != NULL)
+ {
+ gtk_box_pack_end (GTK_BOX (box), switcher, FALSE, FALSE, 0);
+ gtk_widget_set_valign (switcher, GTK_ALIGN_CENTER);
+ }
+
+ event_box = gtk_event_box_new ();
+ gtk_container_add (GTK_CONTAINER (event_box), box);
+ row = gtk_list_box_row_new ();
+ gtk_container_add (GTK_CONTAINER (row), event_box);
+
+ return row;
+}
+
+static void
+search_panel_add_one_app_info (CcSearchPanel *self,
+ GAppInfo *app_info,
+ gboolean default_enabled)
+{
+ GtkWidget *row, *w;
+
+ /* gnome-control-center is special cased in the shell,
+ and is not configurable */
+ if (g_strcmp0 (g_app_info_get_id (app_info),
+ "gnome-control-center.desktop") == 0)
+ return;
+
+ /* reset valignment of the list box */
+ gtk_widget_set_valign (self->priv->list_box, GTK_ALIGN_FILL);
+
w = gtk_switch_new ();
- gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
- gtk_box_pack_end (GTK_BOX (box), w, FALSE, FALSE, 0);
+ row = create_row (app_info, w);
+ g_object_set_data_full (G_OBJECT (row), "app-info",
+ g_object_ref (app_info), g_object_unref);
+ g_object_set_data (G_OBJECT (row), "self", self);
+ gtk_container_add (GTK_CONTAINER (self->priv->list_box), row);
if (default_enabled)
{
@@ -495,6 +539,11 @@ 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);
}
@@ -737,6 +786,45 @@ cc_search_panel_constructed (GObject *object)
cc_shell_embed_widget_in_header (cc_panel_get_shell (CC_PANEL (self)), box);
}
+static GtkWidget *
+create_placeholder_row (gpointer item,
+ gpointer user_data)
+{
+ GtkWidget *row, *w;
+ GAppInfo *app_info;
+
+ app_info = g_object_get_data (G_OBJECT (item), "app-info");
+
+ w = gtk_switch_new ();
+ gtk_switch_set_active (GTK_SWITCH (w), TRUE);
+
+ row = create_row (app_info, w);
+ gtk_style_context_add_class (gtk_widget_get_style_context (row), "view");
+
+ w = gtk_frame_new (NULL);
+ gtk_container_add (GTK_CONTAINER (w), row);
+
+ return w;
+}
+
+static void
+on_row_moved (DndListBox *listbox,
+ GtkListBoxRow *row,
+ CcSearchPanel *self)
+{
+ GAppInfo *app_info;
+ const char *app_info_id;
+ gint idx;
+
+ idx = gtk_list_box_row_get_index (row);
+ app_info = g_object_get_data (G_OBJECT (row), "app-info");
+ app_info_id = g_app_info_get_id (app_info);
+
+ g_hash_table_replace (self->priv->sort_order, g_strdup (app_info_id), GINT_TO_POINTER (idx));
+
+ search_panel_propagate_sort_order (self);
+}
+
static void
cc_search_panel_init (CcSearchPanel *self)
{
@@ -764,7 +852,9 @@ cc_search_panel_init (CcSearchPanel *self)
}
frame = WID ("search_frame");
- widget = GTK_WIDGET (gtk_list_box_new ());
+ widget = GTK_WIDGET (dnd_list_box_new ());
+ dnd_list_box_set_create_placeholder_func (DND_LIST_BOX (widget), create_placeholder_row, NULL,
g_object_unref);
+ g_signal_connect (widget, "row-moved", G_CALLBACK (on_row_moved), self);
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..cd9a77a
--- /dev/null
+++ b/panels/search/dndlistbox.c
@@ -0,0 +1,474 @@
+/* dndlistbox.c
+ *
+ * Copyright (C) 2016 Felipe Borges <felipeborges gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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;
+
+ DndListBoxCreatePlaceholderFunc create_placeholder_func;
+ gpointer create_placeholder_func_data;
+ GDestroyNotify create_placeholder_func_data_destroy;
+};
+
+G_DEFINE_TYPE (DndListBox, dnd_list_box, GTK_TYPE_LIST_BOX)
+
+enum {
+ ROW_MOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+#define ROW_OUTSIDE_LISTBOX -1
+
+GtkWidget *
+dnd_list_box_new (void)
+{
+ return g_object_new (DND_TYPE_LIST_BOX, NULL);
+}
+
+void
+dnd_list_box_set_create_placeholder_func (DndListBox *self,
+ DndListBoxCreatePlaceholderFunc func,
+ gpointer user_data,
+ GDestroyNotify destroy)
+{
+ g_return_if_fail (DND_IS_LIST_BOX (self));
+
+ if (self->create_placeholder_func_data_destroy)
+ self->create_placeholder_func_data_destroy (self->create_placeholder_func_data);
+
+ self->create_placeholder_func = func;
+ self->create_placeholder_func_data = user_data;
+ self->create_placeholder_func_data_destroy = destroy;
+}
+
+void
+dnd_list_box_set_drag_row (DndListBox *self,
+ GtkWidget *row,
+ GdkEventButton *event)
+{
+ g_return_if_fail (DND_IS_LIST_BOX (self));
+
+ 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);
+ 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;
+
+ 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))
+ {
+ GtkWidget *row;
+ gint position;
+
+ position = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (*source_row));
+
+ row = g_object_ref (gtk_list_box_get_row_at_index (GTK_LIST_BOX (self), position));
+ gtk_container_remove (GTK_CONTAINER (self), row);
+
+ if (self->row_destination_index > source_index)
+ gtk_list_box_insert (GTK_LIST_BOX (self), row, self->row_destination_index - 1);
+ else
+ gtk_list_box_insert (GTK_LIST_BOX (self), row, self->row_destination_index);
+
+ g_signal_emit (self, signals[ROW_MOVED], 0, row);
+ }
+ }
+
+ gtk_drag_finish (context, FALSE, FALSE, time);
+
+ if (self->row_placeholder)
+ {
+ gtk_widget_destroy (self->row_placeholder);
+ self->row_placeholder = NULL;
+ }
+}
+
+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;
+
+ 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;
+ }
+
+ 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_style_context_add_class (gtk_widget_get_style_context (row), "background");
+ gtk_widget_set_opacity (row, 0.6);
+ 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, GTK_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_all (self->row_placeholder);
+ g_object_ref_sink (self->row_placeholder);
+ }
+ else if (GTK_WIDGET (generic_row) == self->row_placeholder)
+ {
+ /* cursor on 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;
+ }
+
+ 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;
+
+ if (self->create_placeholder_func)
+ row = self->create_placeholder_func (self->drag_row, GINT_TO_POINTER (self->drag_row_height));
+ else
+ row = create_placeholder_row (self->drag_row_height);
+
+ 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_clear_object (&self->dnd_window);
+ g_clear_object (&self->drag_row);
+
+ 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;
+
+ signals[ROW_MOVED] =
+ g_signal_new ("row-moved",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_LIST_BOX_ROW);
+}
+
+static const GtkTargetEntry targets [] = {
+ { "GTK_LIST_BOX_ROW", GTK_TARGET_SAME_WIDGET, 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..d76486f
--- /dev/null
+++ b/panels/search/dndlistbox.h
@@ -0,0 +1,57 @@
+/* dndlistbox.h
+ *
+ * Copyright (C) 2016 Felipe Borges <felipeborges gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef DND_LIST_BOX_H
+#define DND_LIST_BOX_H
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define DND_TYPE_LIST_BOX (dnd_list_box_get_type())
+
+G_DECLARE_FINAL_TYPE (DndListBox, dnd_list_box, DND, LIST_BOX, GtkListBox)
+
+struct _DndListBoxClass
+{
+ GtkListBoxClass parent_class;
+
+ void (*row_moved) (DndListBox *box,
+ GtkListBoxRow *row);
+};
+
+typedef GtkWidget * (*DndListBoxCreatePlaceholderFunc) (gpointer item,
+ gpointer user_data);
+
+GtkWidget *dnd_list_box_new (void);
+
+void dnd_list_box_set_drag_row (DndListBox *self,
+ GtkWidget *row,
+ GdkEventButton *event);
+
+void dnd_list_box_set_create_placeholder_func (DndListBox *self,
+ DndListBoxCreatePlaceholderFunc func,
+ gpointer user_data,
+ GDestroyNotify destroy);
+
+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]