[gnome-control-center/wip/feborges/new-search-panel: 4/4] search: FIXME commit message: introduce Drag and Drop
- From: Felipe Borges <felipeborges src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center/wip/feborges/new-search-panel: 4/4] search: FIXME commit message: introduce Drag and Drop
- Date: Thu, 27 Oct 2016 08:20:51 +0000 (UTC)
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]