[gnome-todo] task-list-view: scroll when dragging over the tasklist borders
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-todo] task-list-view: scroll when dragging over the tasklist borders
- Date: Wed, 26 Oct 2016 01:18:28 +0000 (UTC)
commit 4799bc777ce58f56bbb4a6a40ce1db1b157b7ab7
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Tue Oct 25 23:14:51 2016 -0200
task-list-view: scroll when dragging over the tasklist borders
When scrolling over the top and bottom areas, we should scroll the
tasklist widget, otherwise some (many?) tasks will be hidden and
unaccessible.
This also has the nice side-effect of fixing the flickering when
moving a task around.
src/gtd-dnd-row.c | 148 +++++++++++++++++++---------------------------
src/gtd-dnd-row.h | 2 -
src/gtd-task-list-view.c | 99 ++++++++++++++++++++++++++++--
3 files changed, 153 insertions(+), 96 deletions(-)
---
diff --git a/src/gtd-dnd-row.c b/src/gtd-dnd-row.c
index 4d9747b..f1b6580 100644
--- a/src/gtd-dnd-row.c
+++ b/src/gtd-dnd-row.c
@@ -33,7 +33,6 @@ struct _GtdDndRow
GtdTaskRow *row_above;
gint depth;
- gboolean has_dnd : 1;
};
G_DEFINE_TYPE (GtdDndRow, gtd_dnd_row, GTK_TYPE_LIST_BOX_ROW)
@@ -117,13 +116,69 @@ gtd_dnd_row_set_property (GObject *object,
}
static void
-gtd_dnd_row_drag_leave (GtkWidget *widget,
- GdkDragContext *context,
- guint time)
+gtd_dnd_row_class_init (GtdDndRowClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtd_dnd_row_finalize;
+ object_class->get_property = gtd_dnd_row_get_property;
+ object_class->set_property = gtd_dnd_row_set_property;
+
+ properties[PROP_ROW_ABOVE] = g_param_spec_object ("row-above",
+ "Row above",
+ "The task row above this row",
+ GTD_TYPE_TASK_ROW,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/dnd-row.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GtdDndRow, box);
+ gtk_widget_class_bind_template_child (widget_class, GtdDndRow, icon);
+
+ gtk_widget_class_set_css_name (widget_class, "dndrow");
+}
+
+static void
+gtd_dnd_row_init (GtdDndRow *self)
{
- GtdDndRow *self = GTD_DND_ROW (widget);
+ gtk_widget_init_template (GTK_WIDGET (self));
- self->has_dnd = FALSE;
+ gtk_drag_dest_set (GTK_WIDGET (self),
+ 0,
+ NULL,
+ 0,
+ GDK_ACTION_MOVE);
+}
+
+GtkWidget*
+gtd_dnd_row_new (void)
+{
+ return g_object_new (GTD_TYPE_DND_ROW, NULL);
+}
+
+GtdTaskRow*
+gtd_dnd_row_get_row_above (GtdDndRow *self)
+{
+ g_return_val_if_fail (GTD_IS_DND_ROW (self), NULL);
+
+ return self->row_above;
+}
+
+void
+gtd_dnd_row_set_row_above (GtdDndRow *self,
+ GtdTaskRow *row)
+{
+ g_return_if_fail (GTD_IS_DND_ROW (self));
+
+ if (g_set_object (&self->row_above, row))
+ {
+ update_row_padding (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ROW_ABOVE]);
+ }
}
gboolean
@@ -153,8 +208,6 @@ gtd_dnd_row_drag_motion (GtkWidget *widget,
self->depth = 0;
}
- self->has_dnd = TRUE;
-
update_row_padding (self);
gdk_drag_status (context, GDK_ACTION_COPY, time);
@@ -175,7 +228,6 @@ gtd_dnd_row_drag_drop (GtkWidget *widget,
GtdTask *row_task, *target_task;
self = GTD_DND_ROW (widget);
- self->has_dnd = FALSE;
/* Reset padding */
update_row_padding (self);
@@ -240,81 +292,3 @@ gtd_dnd_row_drag_drop (GtkWidget *widget,
return TRUE;
}
-static void
-gtd_dnd_row_class_init (GtdDndRowClass *klass)
-{
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->finalize = gtd_dnd_row_finalize;
- object_class->get_property = gtd_dnd_row_get_property;
- object_class->set_property = gtd_dnd_row_set_property;
-
- widget_class->drag_drop = gtd_dnd_row_drag_drop;
- widget_class->drag_leave = gtd_dnd_row_drag_leave;
- widget_class->drag_motion = gtd_dnd_row_drag_motion;
-
- properties[PROP_ROW_ABOVE] = g_param_spec_object ("row-above",
- "Row above",
- "The task row above this row",
- GTD_TYPE_TASK_ROW,
- G_PARAM_READWRITE);
-
- g_object_class_install_properties (object_class, N_PROPS, properties);
-
- gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/dnd-row.ui");
-
- gtk_widget_class_bind_template_child (widget_class, GtdDndRow, box);
- gtk_widget_class_bind_template_child (widget_class, GtdDndRow, icon);
-
- gtk_widget_class_set_css_name (widget_class, "dndrow");
-}
-
-static void
-gtd_dnd_row_init (GtdDndRow *self)
-{
- gtk_widget_init_template (GTK_WIDGET (self));
-
- gtk_drag_dest_set (GTK_WIDGET (self),
- 0,
- NULL,
- 0,
- GDK_ACTION_MOVE);
-}
-
-GtkWidget*
-gtd_dnd_row_new (void)
-{
- return g_object_new (GTD_TYPE_DND_ROW, NULL);
-}
-
-GtdTaskRow*
-gtd_dnd_row_get_row_above (GtdDndRow *self)
-{
- g_return_val_if_fail (GTD_IS_DND_ROW (self), NULL);
-
- return self->row_above;
-}
-
-void
-gtd_dnd_row_set_row_above (GtdDndRow *self,
- GtdTaskRow *row)
-{
- g_return_if_fail (GTD_IS_DND_ROW (self));
-
- if (g_set_object (&self->row_above, row))
- {
- update_row_padding (self);
-
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ROW_ABOVE]);
- }
-}
-
-gboolean
-gtd_dnd_row_has_dnd (GtdDndRow *self)
-{
- g_return_val_if_fail (GTD_IS_DND_ROW (self), FALSE);
-
- return self->has_dnd;
-}
-
diff --git a/src/gtd-dnd-row.h b/src/gtd-dnd-row.h
index d52775d..4c6da9b 100644
--- a/src/gtd-dnd-row.h
+++ b/src/gtd-dnd-row.h
@@ -49,8 +49,6 @@ gboolean gtd_dnd_row_drag_motion (GtkWidget
gint y,
guint time);
-gboolean gtd_dnd_row_has_dnd (GtdDndRow *self);
-
G_END_DECLS
#endif /* GTD_DND_ROW_H */
diff --git a/src/gtd-task-list-view.c b/src/gtd-task-list-view.c
index a816051..54ea640 100644
--- a/src/gtd-task-list-view.c
+++ b/src/gtd-task-list-view.c
@@ -73,7 +73,8 @@ typedef struct
GtkRevealer *revealer;
GtkImage *done_image;
GtkLabel *done_label;
- GtkScrolledWindow *viewport;
+ GtkWidget *viewport;
+ GtkWidget *scrolled_window;
/* internal */
gboolean can_toggle;
@@ -85,6 +86,10 @@ typedef struct
GtdTaskList *task_list;
GDateTime *default_date;
+ /* DnD autoscroll */
+ guint scroll_timeout_id;
+ gboolean scroll_up : 1;
+
/* color provider */
GtkCssProvider *color_provider;
GdkRGBA *color;
@@ -114,6 +119,8 @@ struct _GtdTaskListView
#define TASK_REMOVED_NOTIFICATION_ID "task-removed-id"
+#define DND_SCROLL_OFFSET 24 // px
+
/* prototypes */
static void gtd_task_list_view__clear_completed_tasks (GSimpleAction *simple,
GVariant *parameter,
@@ -1123,7 +1130,7 @@ gtd_task_list_view_constructed (GObject *object)
/* css provider */
self->priv->color_provider = gtk_css_provider_new ();
- gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (self->priv->viewport)),
+ gtk_style_context_add_provider (gtk_widget_get_style_context (self->priv->viewport),
GTK_STYLE_PROVIDER (self->priv->color_provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 2);
@@ -1137,6 +1144,70 @@ gtd_task_list_view_constructed (GObject *object)
/*
* Listbox Drag n' Drop functions
*/
+static inline gboolean
+scroll_to_dnd (gpointer user_data)
+{
+ GtdTaskListViewPrivate *priv;
+ GtkAdjustment *vadjustment;
+ gint value;
+
+ priv = gtd_task_list_view_get_instance_private (user_data);
+ vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolled_window));
+ value = gtk_adjustment_get_value (vadjustment) + (priv->scroll_up ? -6 : 6);
+
+ gtk_adjustment_set_value (vadjustment,
+ CLAMP (value, 0, gtk_adjustment_get_upper (vadjustment)));
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+check_dnd_scroll (GtdTaskListView *self,
+ gboolean should_cancel,
+ gint y)
+{
+ GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self);
+ gint current_y, height;
+
+ if (should_cancel)
+ {
+ if (priv->scroll_timeout_id > 0)
+ {
+ g_source_remove (priv->scroll_timeout_id);
+ priv->scroll_timeout_id = 0;
+ }
+
+ return;
+ }
+
+ height = gtk_widget_get_allocated_height (priv->scrolled_window);
+ gtk_widget_translate_coordinates (GTK_WIDGET (priv->listbox),
+ priv->scrolled_window,
+ 0, y,
+ NULL, ¤t_y);
+
+ if (current_y < DND_SCROLL_OFFSET || current_y > height - DND_SCROLL_OFFSET)
+ {
+ if (priv->scroll_timeout_id > 0)
+ return;
+
+ /* Start the autoscroll */
+ priv->scroll_up = current_y < DND_SCROLL_OFFSET;
+ priv->scroll_timeout_id = g_timeout_add (25,
+ scroll_to_dnd,
+ self);
+ }
+ else
+ {
+ if (priv->scroll_timeout_id == 0)
+ return;
+
+ /* Cancel the autoscroll */
+ g_source_remove (priv->scroll_timeout_id);
+ priv->scroll_timeout_id = 0;
+ }
+}
+
static void
listbox_drag_leave (GtkListBox *listbox,
GdkDragContext *context,
@@ -1147,7 +1218,9 @@ listbox_drag_leave (GtkListBox *listbox,
priv = gtd_task_list_view_get_instance_private (self);
- gtk_widget_set_visible (priv->dnd_row, gtd_dnd_row_has_dnd (GTD_DND_ROW (priv->dnd_row)));
+ gtk_widget_set_visible (priv->dnd_row, FALSE);
+
+ check_dnd_scroll (self, TRUE, -1);
gtk_list_box_invalidate_sort (listbox);
}
@@ -1173,7 +1246,7 @@ listbox_drag_motion (GtkListBox *listbox,
* drop target. Otherwise, the user can drop at the space after the rows, and the row
* that started the DnD operation is hidden forever.
*/
- if (!hovered_row || GTD_IS_DND_ROW (hovered_row))
+ if (!hovered_row)
{
gtk_widget_hide (priv->dnd_row);
gtd_dnd_row_set_row_above (GTD_DND_ROW (priv->dnd_row), NULL);
@@ -1181,6 +1254,13 @@ listbox_drag_motion (GtkListBox *listbox,
goto success;
}
+ /*
+ * Hovering the DnD row is perfectly valid, but we don't gather the
+ * related row - simply succeed.
+ */
+ if (GTD_IS_DND_ROW (hovered_row))
+ goto success;
+
row_above_dnd = NULL;
task_row = GTD_TASK_ROW (hovered_row);
row_height = gtk_widget_get_allocated_height (GTK_WIDGET (hovered_row));
@@ -1252,6 +1332,7 @@ listbox_drag_motion (GtkListBox *listbox,
gtd_dnd_row_set_row_above (GTD_DND_ROW (priv->dnd_row), row_above_dnd);
+success:
/*
* Also pass the current motion to the DnD row, so it correctly
* adjusts itself - even when the DnD is hovering another row.
@@ -1262,12 +1343,13 @@ listbox_drag_motion (GtkListBox *listbox,
y,
time);
-success:
+ check_dnd_scroll (self, FALSE, y);
+
gdk_drag_status (context, GDK_ACTION_COPY, time);
+
return TRUE;
fail:
- gdk_drag_status (context, 0, time);
return FALSE;
}
@@ -1289,6 +1371,8 @@ listbox_drag_drop (GtkWidget *widget,
y,
time);
+ check_dnd_scroll (self, TRUE, -1);
+
return TRUE;
}
@@ -1413,6 +1497,7 @@ gtd_task_list_view_class_init (GtdTaskListViewClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, done_label);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, new_task_row);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, viewport);
+ gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, scrolled_window);
gtk_widget_class_bind_template_callback (widget_class, gtd_task_list_view__create_task);
gtk_widget_class_bind_template_callback (widget_class, gtd_task_list_view__done_button_clicked);
@@ -1439,7 +1524,7 @@ gtd_task_list_view_init (GtdTaskListView *self)
0,
NULL,
0,
- GDK_ACTION_MOVE);
+ GDK_ACTION_COPY);
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]