[gnome-todo/wip/gbsneto/subtasks: 17/27] task-list, task-list-view: manage drag and drop with GtdDndRow
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-todo/wip/gbsneto/subtasks: 17/27] task-list, task-list-view: manage drag and drop with GtdDndRow
- Date: Fri, 14 Oct 2016 14:42:34 +0000 (UTC)
commit 9360e48331874005392b4d3cfa3e30152bd0637e
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Fri Oct 14 01:30:04 2016 -0300
task-list, task-list-view: manage drag and drop with GtdDndRow
data/ui/list-view.ui | 4 +
src/gtd-task-list-view.c | 204 ++++++++++++++++++++++++++++++++++++++++++++--
src/gtd-task-row.c | 143 +++++++-------------------------
src/gtd-task-row.h | 3 +
4 files changed, 235 insertions(+), 119 deletions(-)
---
diff --git a/data/ui/list-view.ui b/data/ui/list-view.ui
index 9710eed..ce997d3 100644
--- a/data/ui/list-view.ui
+++ b/data/ui/list-view.ui
@@ -47,6 +47,10 @@
<signal name="create-task" handler="gtd_task_list_view__create_task"
object="GtdTaskListView" swapped="no" />
</object>
</child>
+ <child>
+ <object class="GtdDndRow" id="dnd_row">
+ </object>
+ </child>
<style>
<class name="transparent" />
</style>
diff --git a/src/gtd-task-list-view.c b/src/gtd-task-list-view.c
index e02a367..71cc805 100644
--- a/src/gtd-task-list-view.c
+++ b/src/gtd-task-list-view.c
@@ -17,6 +17,7 @@
*/
#include "gtd-arrow-frame.h"
+#include "gtd-dnd-row.h"
#include "gtd-edit-pane.h"
#include "gtd-task-list-view.h"
#include "gtd-manager.h"
@@ -62,6 +63,7 @@
typedef struct
{
GtdArrowFrame *arrow_frame;
+ GtkWidget *dnd_row;
GtdEditPane *edit_pane;
GtkRevealer *edit_revealer;
GtkWidget *empty_box;
@@ -506,13 +508,9 @@ gtd_task_list_view__done_button_clicked (GtkButton *button,
}
static gint
-gtd_task_list_view__listbox_sort_func (GtdTaskRow *row1,
- GtdTaskRow *row2,
- gpointer user_data)
+compare_task_rows (GtdTaskRow *row1,
+ GtdTaskRow *row2)
{
- g_return_val_if_fail (GTD_IS_TASK_ROW (row1), 0);
- g_return_val_if_fail (GTD_IS_TASK_ROW (row2), 0);
-
if (gtd_task_row_get_new_task_mode (row1))
return 1;
else if (gtd_task_row_get_new_task_mode (row2))
@@ -521,6 +519,47 @@ gtd_task_list_view__listbox_sort_func (GtdTaskRow *row1,
return gtd_task_compare (gtd_task_row_get_task (row1), gtd_task_row_get_task (row2));
}
+static gint
+compare_dnd_rows (GtkListBoxRow *row1,
+ GtkListBoxRow *row2)
+{
+ GtdTaskRow *row_above, *current_row;
+ gboolean reverse;
+
+ if (GTD_IS_DND_ROW (row1))
+ {
+ row_above = gtd_dnd_row_get_row_above (GTD_DND_ROW (row1));
+ current_row = GTD_TASK_ROW (row2);
+ reverse = FALSE;
+ }
+ else
+ {
+ row_above = gtd_dnd_row_get_row_above (GTD_DND_ROW (row2));
+ current_row = GTD_TASK_ROW (row1);
+ reverse = TRUE;
+ }
+
+ if (!row_above)
+ return reverse ? 1 : -1;
+
+ if (current_row == row_above)
+ return reverse ? -1 : 1;
+
+ return compare_task_rows (current_row, row_above) * (reverse ? 1 : -1);
+}
+
+static gint
+gtd_task_list_view__listbox_sort_func (GtkListBoxRow *row1,
+ GtkListBoxRow *row2,
+ gpointer user_data)
+{
+ /* Automagically manage the DnD row */
+ if (GTD_IS_DND_ROW (row1) || GTD_IS_DND_ROW (row2))
+ return compare_dnd_rows (row1, row2);
+
+ return compare_task_rows (GTD_TASK_ROW (row1), GTD_TASK_ROW (row2));
+}
+
static void
remove_task (GtdTaskListView *view,
GtdTask *task)
@@ -535,6 +574,9 @@ remove_task (GtdTaskListView *view,
for (l = children; l != NULL; l = l->next)
{
+ if (!GTD_IS_TASK_ROW (l->data))
+ continue;
+
if (l->data != priv->new_task_row &&
gtd_task_row_get_task (l->data) == task)
{
@@ -620,6 +662,9 @@ gtd_task_list_view__remove_row_for_task (GtdTaskListView *view,
for (l = children; l != NULL; l = l->next)
{
+ if (!GTD_IS_TASK_ROW (l->data))
+ continue;
+
if (!gtd_task_row_get_new_task_mode (l->data) &&
gtd_task_row_get_task (l->data) == task)
{
@@ -849,6 +894,133 @@ gtd_task_list_view_constructed (GObject *object)
}
static void
+gtd_task_list_view_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+ GtdTaskListViewPrivate *priv;
+
+ priv = gtd_task_list_view_get_instance_private (GTD_TASK_LIST_VIEW (widget));
+
+ gtk_widget_set_visible (priv->dnd_row, gtd_dnd_row_has_dnd (GTD_DND_ROW (priv->dnd_row)));
+
+ gtk_list_box_invalidate_sort (priv->listbox);
+}
+
+
+static gboolean
+gtd_task_list_view_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GtdTaskListViewPrivate *priv;
+ GtkListBoxRow *hovered_row;
+ GtdTaskRow *task_row, *row_above_dnd, *current_above_row;
+ gint row_x, row_y, row_height;
+
+ priv = gtd_task_list_view_get_instance_private (GTD_TASK_LIST_VIEW (widget));
+ hovered_row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (priv->listbox), y);
+
+ if (!hovered_row || GTD_IS_DND_ROW (hovered_row))
+ {
+ gdk_drag_status (context, 0, time);
+ return FALSE;
+ }
+
+ task_row = GTD_TASK_ROW (hovered_row);
+
+ if (!gtd_task_row_is_drag_valid (task_row, context))
+ {
+ gdk_drag_status (context, 0, time);
+ return FALSE;
+ }
+
+ row_above_dnd = NULL;
+ row_height = gtk_widget_get_allocated_height (GTK_WIDGET (hovered_row));
+ gtk_widget_translate_coordinates (widget,
+ GTK_WIDGET (hovered_row),
+ x, y,
+ &row_x, &row_y);
+
+ gtk_widget_show (priv->dnd_row);
+
+ row_above_dnd = task_row;
+ current_above_row = gtd_dnd_row_get_row_above (GTD_DND_ROW (priv->dnd_row));
+
+ /*
+ * If the pointer if in the top part of the row, move the DnD row to
+ * the previous row.
+ */
+ if (row_y < row_height / 2)
+ {
+ GtkListBoxRow *tmp;
+ gint row_index;
+
+ row_index = gtk_list_box_row_get_index (hovered_row);
+ tmp = NULL;
+
+ /* Search for a valid task row */
+ while (!GTD_IS_TASK_ROW (tmp) && row_index >= 0)
+ tmp = gtk_list_box_get_row_at_index (GTK_LIST_BOX (priv->listbox), --row_index);
+
+ row_above_dnd = GTD_TASK_ROW (tmp);
+ }
+ else
+ {
+ row_above_dnd = task_row;
+ }
+
+
+ /*
+ * Check it again here because row_above_dnd might be the row that started this
+ * Drag n' Drop
+ */
+ if (row_above_dnd && !gtd_task_row_is_drag_valid (row_above_dnd, context))
+ {
+ gdk_drag_status (context, 0, time);
+ return FALSE;
+ }
+
+ if (current_above_row != row_above_dnd)
+ {
+ gtd_dnd_row_set_row_above (GTD_DND_ROW (priv->dnd_row), row_above_dnd);
+ gtk_list_box_invalidate_sort (priv->listbox);
+ }
+
+ gdk_drag_status (context, GDK_ACTION_COPY, time);
+
+ return TRUE;
+}
+
+static gboolean
+gtd_task_list_view_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GtdTaskListViewPrivate *priv;
+ GtkWidget *source_widget, *row;
+
+ priv = gtd_task_list_view_get_instance_private (GTD_TASK_LIST_VIEW (widget));
+
+ gtk_widget_hide (priv->dnd_row);
+
+ /*
+ * When the drag operation began, the source row was hidden. Now is the time
+ * to show it again.
+ */
+ source_widget = gtk_drag_get_source_widget (context);
+ row = gtk_widget_get_ancestor (source_widget, GTK_TYPE_LIST_BOX_ROW);
+
+ gtk_widget_show (row);
+
+ return TRUE;
+}
+
+static void
gtd_task_list_view_map (GtkWidget *widget)
{
GtdTaskListViewPrivate *priv;
@@ -881,6 +1053,9 @@ gtd_task_list_view_class_init (GtdTaskListViewClass *klass)
object_class->get_property = gtd_task_list_view_get_property;
object_class->set_property = gtd_task_list_view_set_property;
+ widget_class->drag_drop = gtd_task_list_view_drag_drop;
+ widget_class->drag_leave = gtd_task_list_view_drag_leave;
+ widget_class->drag_motion = gtd_task_list_view_drag_motion;
widget_class->map = gtd_task_list_view_map;
g_type_ensure (GTD_TYPE_TASK_ROW);
@@ -945,6 +1120,7 @@ gtd_task_list_view_class_init (GtdTaskListViewClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/list-view.ui");
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, arrow_frame);
+ gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, dnd_row);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, edit_pane);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, edit_revealer);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, empty_box);
@@ -971,6 +1147,12 @@ gtd_task_list_view_init (GtdTaskListView *self)
self->priv->can_toggle = TRUE;
gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_drag_dest_set (GTK_WIDGET (self),
+ 0,
+ NULL,
+ 0,
+ GDK_ACTION_MOVE);
}
/**
@@ -1231,7 +1413,12 @@ gtd_task_list_view_set_show_list_name (GtdTaskListView *view,
children = gtk_container_get_children (GTK_CONTAINER (view->priv->listbox));
for (l = children; l != NULL; l = l->next)
- gtd_task_row_set_list_name_visible (l->data, show_list_name);
+ {
+ if (!GTD_IS_TASK_ROW (l->data))
+ continue;
+
+ gtd_task_row_set_list_name_visible (l->data, show_list_name);
+ }
g_list_free (children);
@@ -1318,6 +1505,9 @@ gtd_task_list_view_set_show_completed (GtdTaskListView *view,
for (l = children; l != NULL; l = l->next)
{
+ if (!GTD_IS_TASK_ROW (l->data))
+ continue;
+
if (!gtd_task_row_get_new_task_mode (l->data) &&
gtd_task_get_complete (gtd_task_row_get_task (l->data)))
{
diff --git a/src/gtd-task-row.c b/src/gtd-task-row.c
index 4ad24f4..8ecb350 100644
--- a/src/gtd-task-row.c
+++ b/src/gtd-task-row.c
@@ -204,6 +204,8 @@ drag_begin_cb (GtkWidget *widget,
drag_x + self->clicked_x,
drag_y + self->clicked_y);
+ gtk_widget_hide (GTK_WIDGET (self));
+
g_clear_object (&pixbuf);
}
@@ -503,108 +505,6 @@ gtd_task_row_set_property (GObject *object,
}
}
-static gboolean
-gtd_task_row_drag_drop (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- GtdProvider *provider;
- GtkWidget *source_widget, *row;
- GtdTask *row_task, *target_task;
-
- row = NULL;
- source_widget = gtk_drag_get_source_widget (context);
-
- if (!source_widget)
- {
- gdk_drag_status (context, 0, time);
- return FALSE;
- }
-
- row = gtk_widget_get_ancestor (source_widget, GTD_TYPE_TASK_ROW);
-
- /* Do not allow dropping on itself, nor on the new task row */
- if (!row || row == widget || GTD_TASK_ROW (row)->new_task_mode)
- {
- gdk_drag_status (context, 0, time);
- return FALSE;
- }
-
- row_task = GTD_TASK_ROW (row)->task;
- target_task = GTD_TASK_ROW (widget)->task;
-
- /* Forbid adding the parent task as a subtask */
- if (gtd_task_is_subtask (row_task, target_task))
- {
- gdk_drag_status (context, 0, time);
- return FALSE;
- }
-
- gtd_task_add_subtask (target_task, row_task);
-
- /* Save the task */
- provider = gtd_task_list_get_provider (gtd_task_get_list (row_task));
-
- gtd_task_save (row_task);
- gtd_provider_update_task (provider, row_task);
-
- return TRUE;
-}
-
-static void
-gtd_task_row_drag_leave (GtkWidget *widget,
- GdkDragContext *context,
- guint time)
-{
- gtk_drag_unhighlight (widget);
-}
-
-static gboolean
-gtd_task_row_drag_motion (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- GtkWidget *source_widget, *row;
- GtdTask *row_task, *target_task;
-
- row = NULL;
- source_widget = gtk_drag_get_source_widget (context);
-
- if (!source_widget)
- {
- gdk_drag_status (context, 0, time);
- return FALSE;
- }
-
- row = gtk_widget_get_ancestor (source_widget, GTD_TYPE_TASK_ROW);
-
- /* Do not allow dropping on itself, nor on the new task row */
- if (!row || row == widget || GTD_TASK_ROW (widget)->new_task_mode)
- {
- gdk_drag_status (context, 0, time);
- return FALSE;
- }
-
- row_task = GTD_TASK_ROW (row)->task;
- target_task = GTD_TASK_ROW (widget)->task;
-
- /* Forbid adding the parent task as a subtask */
- if (gtd_task_is_subtask (row_task, target_task))
- {
- gdk_drag_status (context, 0, time);
- return FALSE;
- }
-
- gdk_drag_status (context, GDK_ACTION_COPY, time);
- gtk_drag_highlight (widget);
-
- return TRUE;
-}
-
static void
gtd_task_row_activate (GtkListBoxRow *row)
{
@@ -624,9 +524,6 @@ gtd_task_row_class_init (GtdTaskRowClass *klass)
object_class->get_property = gtd_task_row_get_property;
object_class->set_property = gtd_task_row_set_property;
- widget_class->drag_drop = gtd_task_row_drag_drop;
- widget_class->drag_leave = gtd_task_row_drag_leave;
- widget_class->drag_motion = gtd_task_row_drag_motion;
widget_class->focus_in_event = gtd_task_row__focus_in;
widget_class->key_press_event = gtd_task_row__key_press_event;
@@ -759,13 +656,6 @@ gtd_task_row_init (GtdTaskRow *self)
NULL,
0,
GDK_ACTION_COPY);
-
- /* And the destination is the row itself */
- gtk_drag_dest_set (GTK_WIDGET (self),
- 0,
- NULL,
- 0,
- GDK_ACTION_MOVE);
}
/**
@@ -977,3 +867,32 @@ gtd_task_row_destroy (GtdTaskRow *self)
self);
}
}
+
+gboolean
+gtd_task_row_is_drag_valid (GtdTaskRow *self,
+ GdkDragContext *context)
+{
+ GtdTaskRow *row;
+ GtkWidget *source_widget;
+ GtdTask *row_task, *target_task;
+
+ source_widget = gtk_drag_get_source_widget (context);
+
+ if (!source_widget)
+ return FALSE;
+
+ row = GTD_TASK_ROW (gtk_widget_get_ancestor (source_widget, GTD_TYPE_TASK_ROW));
+
+ /* Do not allow dropping on itself, nor on the new task row */
+ if (row == self || self->new_task_mode)
+ return FALSE;
+
+ row_task = row->task;
+ target_task = self->task;
+
+ /* Forbid adding the parent task as a subtask */
+ if (gtd_task_is_subtask (row_task, target_task))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/gtd-task-row.h b/src/gtd-task-row.h
index 4d536d1..cabbbe4 100644
--- a/src/gtd-task-row.h
+++ b/src/gtd-task-row.h
@@ -49,6 +49,9 @@ void gtd_task_row_reveal (GtdTaskRow
void gtd_task_row_destroy (GtdTaskRow *row);
+gboolean gtd_task_row_is_drag_valid (GtdTaskRow *self,
+ GdkDragContext *context);
+
G_END_DECLS
#endif /* GTD_TASK_ROW_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]