[gtk+/wip/feborges/listboxdnd: 1069/1070] listbox: Introduce the Drag and Drop API
- From: Felipe Borges <felipeborges src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/feborges/listboxdnd: 1069/1070] listbox: Introduce the Drag and Drop API
- Date: Mon, 16 Oct 2017 12:44:39 +0000 (UTC)
commit ba0c8ee7581d28fc871e562aa87c9ef1c5ec2aae
Author: Felipe Borges <felipeborges gnome org>
Date: Mon Oct 16 14:11:50 2017 +0200
listbox: Introduce the Drag and Drop API
gtk/gtklistbox.c | 311 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
gtk/gtklistbox.h | 5 +
2 files changed, 315 insertions(+), 1 deletions(-)
---
diff --git a/gtk/gtklistbox.c b/gtk/gtklistbox.c
index 702e342..9d7f279 100644
--- a/gtk/gtklistbox.c
+++ b/gtk/gtklistbox.c
@@ -111,6 +111,7 @@ typedef struct
GtkGesture *multipress_gesture;
/* DnD */
+ gboolean reorderable;
GtkListBoxRow *drag_highlighted_row;
int n_visible_rows;
@@ -156,6 +157,7 @@ enum {
PROP_0,
PROP_SELECTION_MODE,
PROP_ACTIVATE_ON_SINGLE_CLICK,
+ PROP_REORDERABLE,
LAST_PROPERTY
};
@@ -310,11 +312,27 @@ static gboolean gtk_list_box_render (GtkCssGadget *gadget,
gpointer data);
+static void gtk_list_box_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint32 time);
+
+static gboolean gtk_list_box_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint time);
static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
static guint signals[LAST_SIGNAL] = { 0 };
static GParamSpec *row_properties[LAST_ROW_PROPERTY] = { NULL, };
static guint row_signals[ROW__LAST_SIGNAL] = { 0 };
+static GtkTargetEntry drag_target_entries[] = {
+ { "GTK_LIST_BOX_ROW", GTK_TARGET_SAME_APP, 0 }
+};
/**
* gtk_list_box_new:
@@ -347,6 +365,9 @@ gtk_list_box_get_property (GObject *obj,
case PROP_ACTIVATE_ON_SINGLE_CLICK:
g_value_set_boolean (value, priv->activate_single_click);
break;
+ case PROP_REORDERABLE:
+ g_value_set_boolean (value, priv->reorderable);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
break;
@@ -369,6 +390,9 @@ gtk_list_box_set_property (GObject *obj,
case PROP_ACTIVATE_ON_SINGLE_CLICK:
gtk_list_box_set_activate_on_single_click (box, g_value_get_boolean (value));
break;
+ case PROP_REORDERABLE:
+ gtk_list_box_set_reorderable (box, g_value_get_boolean (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
break;
@@ -478,6 +502,13 @@ gtk_list_box_class_init (GtkListBoxClass *klass)
TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+ properties[PROP_REORDERABLE] =
+ g_param_spec_boolean ("reorderable",
+ P_("Reorderable"),
+ P_("List is reorderable"),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
/**
@@ -681,7 +712,6 @@ gtk_list_box_init (GtkListBox *box)
gtk_list_box_render,
NULL,
NULL);
-
}
/**
@@ -1140,6 +1170,158 @@ gtk_list_box_get_selection_mode (GtkListBox *box)
return BOX_PRIV (box)->selection_mode;
}
+static void
+gtk_list_box_row_drag_begin (GtkWidget *widget,
+ GdkDragContext *context,
+ gpointer data)
+{
+ GtkWidget *row;
+ GtkAllocation alloc;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int x, y;
+
+ row = gtk_widget_get_ancestor (widget, GTK_TYPE_LIST_BOX_ROW);
+ gtk_widget_get_allocation (row, &alloc);
+ surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
+ CAIRO_CONTENT_COLOR_ALPHA,
+ alloc.width, alloc.height);
+ cr = cairo_create (surface);
+
+ gtk_widget_draw (row, cr);
+
+ gtk_widget_translate_coordinates (widget, row, 0, 0, &x, &y);
+ cairo_surface_set_device_offset (surface, -x, -y);
+ gtk_drag_set_icon_surface (context, surface);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ g_object_set_data (G_OBJECT (gtk_widget_get_parent (row)), "drag-row", row);
+}
+
+static void
+gtk_list_box_row_drag_end (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ GtkWidget *row;
+
+ row = gtk_widget_get_ancestor (widget, GTK_TYPE_LIST_BOX_ROW);
+ g_object_set_data (G_OBJECT (gtk_widget_get_parent (row)), "drag-row", NULL);
+}
+
+static void
+gtk_list_box_row_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ gpointer data)
+{
+ gtk_selection_data_set (selection_data,
+ gdk_atom_intern_static_string ("GTK_LIST_BOX_ROW"),
+ 32,
+ (const guchar *)&widget,
+ sizeof (gpointer));
+}
+
+static void
+gtk_list_box_row_set_draggable (GtkListBoxRow *row,
+ gboolean reorderable)
+{
+ GtkWidget *child;
+ GtkWidget *ebox;
+ GtkWidget *hbox;
+
+ if (reorderable)
+ {
+ child = gtk_bin_get_child (GTK_BIN (row));
+ g_object_ref (child);
+ gtk_container_remove (GTK_CONTAINER (row), child);
+ ebox = gtk_event_box_new ();
+ gtk_container_add (GTK_CONTAINER (ebox),
+ gtk_image_new_from_icon_name ("open-menu-symbolic",
+ GTK_ICON_SIZE_MENU));
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_container_add (GTK_CONTAINER (hbox), ebox);
+ gtk_container_add (GTK_CONTAINER (hbox), child);
+
+ gtk_container_add (GTK_CONTAINER (row), hbox);
+
+ g_object_set_data (G_OBJECT (row), "child", child);
+
+ gtk_drag_source_set (ebox, GDK_BUTTON1_MASK, drag_target_entries, 1, GDK_ACTION_MOVE);
+ g_signal_connect (ebox, "drag-begin", G_CALLBACK (gtk_list_box_row_drag_begin), NULL);
+ g_signal_connect (ebox, "drag-end", G_CALLBACK (gtk_list_box_row_drag_end), NULL);
+ g_signal_connect (ebox, "drag-data-get", G_CALLBACK (gtk_list_box_row_drag_data_get), NULL);
+ }
+ else
+ {
+ GList *l, *children;
+
+ child = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "child"));
+ g_object_ref (child);
+
+ children = gtk_container_get_children (GTK_CONTAINER (row));
+ for (l = children; l != NULL; l = l->next)
+ gtk_widget_destroy (GTK_WIDGET (l->data));
+
+ gtk_container_add (GTK_CONTAINER (row), child);
+ }
+
+ g_object_unref (child);
+
+ gtk_widget_show_all (GTK_WIDGET (row));
+}
+
+/**
+ * gtk_list_box_set_reorderable:
+ * @box: a #GtkListBox
+ * @reorderble: %TRUE, if the list can be reordered.
+ *
+ * This function doesn't touch binded models.
+ *
+ */
+void
+gtk_list_box_set_reorderable (GtkListBox *box,
+ gboolean reorderable)
+{
+ g_return_if_fail (GTK_IS_LIST_BOX (box));
+
+ reorderable = reorderable != FALSE;
+
+ if (BOX_PRIV (box)->reorderable == reorderable)
+ return;
+
+ if (reorderable)
+ {
+ gtk_drag_dest_set (GTK_WIDGET (box),
+ GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+ drag_target_entries, 1,
+ GDK_ACTION_MOVE);
+ g_signal_connect (box, "drag-data-received",
+ G_CALLBACK (gtk_list_box_drag_data_received), NULL);
+ g_signal_connect (box, "drag-motion",
+ G_CALLBACK (gtk_list_box_drag_motion), NULL);
+ }
+
+ gtk_container_foreach (GTK_CONTAINER (box),
+ (GtkCallback) gtk_list_box_row_set_draggable,
+ GINT_TO_POINTER (reorderable));
+
+ BOX_PRIV (box)->reorderable = reorderable;
+
+ g_object_notify_by_pspec (G_OBJECT (box), properties[PROP_REORDERABLE]);
+}
+
+gboolean
+gtk_list_box_get_reorderable (GtkListBox *box)
+{
+ g_return_val_if_fail (GTK_IS_LIST_BOX (box), FALSE);
+
+ return BOX_PRIV (box)->reorderable;
+}
+
/**
* gtk_list_box_set_filter_func:
* @box: a #GtkListBox
@@ -2872,6 +3054,133 @@ gtk_list_box_insert_css_node (GtkListBox *box,
sibling);
}
+static GtkListBoxRow *
+gtk_list_box_get_last_row (GtkListBox *box)
+{
+ guint pos = g_sequence_get_length (BOX_PRIV (box)->children) - 1;
+
+ return gtk_list_box_get_row_at_index (box, pos);
+}
+
+static GtkListBoxRow *
+gtk_list_box_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 *
+gtk_list_box_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
+gtk_list_box_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint32 time)
+{
+ GtkWidget *row_before;
+ GtkWidget *row_after;
+ GtkWidget *row;
+ GtkWidget *source;
+ gboolean success = TRUE;
+ int pos;
+
+ row_before = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-before"));
+ row_after = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-after"));
+
+ g_object_set_data (G_OBJECT (widget), "row-before", NULL);
+ g_object_set_data (G_OBJECT (widget), "row-after", NULL);
+
+ row = (gpointer)* (gpointer*)gtk_selection_data_get_data (selection_data);
+ source = gtk_widget_get_ancestor (row, GTK_TYPE_LIST_BOX_ROW);
+
+ if (source == row_after)
+ {
+ success = FALSE;
+ goto out;
+ }
+
+ 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));
+ else
+ pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (row_before)) + 1;
+
+ gtk_list_box_insert (GTK_LIST_BOX (widget), source, pos);
+ g_object_unref (source);
+
+out:
+ gtk_drag_finish (context, success, TRUE, time);
+}
+
+static gboolean
+gtk_list_box_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint time)
+{
+ GtkAllocation alloc;
+ GtkWidget *row;
+ int hover_row_y;
+ int hover_row_height;
+ GtkWidget *drag_row;
+ GtkWidget *row_before;
+ GtkWidget *row_after;
+
+ row = GTK_WIDGET (gtk_list_box_get_row_at_y (GTK_LIST_BOX (widget), y));
+
+ drag_row = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "drag-row"));
+ row_before = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-before"));
+ row_after = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-after"));
+
+ 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)
+ {
+ row_after = row;
+ row_before = GTK_WIDGET (gtk_list_box_get_row_before (GTK_LIST_BOX (widget), GTK_LIST_BOX_ROW
(row)));
+ }
+ else
+ {
+ row_before = row;
+ row_after = GTK_WIDGET (gtk_list_box_get_row_after (GTK_LIST_BOX (widget), GTK_LIST_BOX_ROW
(row)));
+ }
+ }
+ else
+ {
+ row_before = GTK_WIDGET (gtk_list_box_get_last_row (GTK_LIST_BOX (widget)));
+ row_after = NULL;
+ }
+
+ g_object_set_data (G_OBJECT (widget), "row-before", row_before);
+ g_object_set_data (G_OBJECT (widget), "row-after", row_after);
+
+ if (drag_row == row_before || drag_row == row_after)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/**
* gtk_list_box_insert:
* @box: a #GtkListBox
diff --git a/gtk/gtklistbox.h b/gtk/gtklistbox.h
index e796b98..78a23fc 100644
--- a/gtk/gtklistbox.h
+++ b/gtk/gtklistbox.h
@@ -313,6 +313,11 @@ void gtk_list_box_bind_model (GtkListBox
gpointer user_data,
GDestroyNotify user_data_free_func);
+void gtk_list_box_set_reorderable (GtkListBox *box,
+ gboolean reorderable);
+
+gboolean gtk_list_box_get_reorderable (GtkListBox *box);
+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListBox, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListBoxRow, g_object_unref)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]