From 7d52dff5ec1ed69cd98715d71c92dff2ef6bdc41 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Fri, 19 Apr 2013 11:00:36 -0500 Subject: [PATCH 1/6] Add a DropState enum to the internal state of the sidebar We want to show a little animation when a DnD operation is happening and the user hovers between existing bookmarks. The animation should indicate the user that he's about to create a new bookmark from the file right there. Signed-off-by: Federico Mena Quintero --- gtk/gtkplacessidebar.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c index 98cd47f1d8..f66b7c05d8 100644 --- a/gtk/gtkplacessidebar.c +++ b/gtk/gtkplacessidebar.c @@ -108,6 +108,26 @@ #define DO_NOT_COMPILE 0 +/* These are used when a destination-side DnD operation is taking place. + * Normally, when a file is being hovered directly over a bookmark, + * we'll be in DROP_STATE_NORMAL. + * + * But when a file is being hovered between bookmarks, this means the user + * may want to create a new bookmark for that file between those bookmarks. + * In that case, the drop state will be *not* DROP_STATE_NORMAL. + * + * When the drop state is FADING_OUT, it means that the user is hovering + * directly over an existing bookmark and an immediate drop will cause the + * file being dragged to be dropped on the bookmark, instead of causing + * a new bookmark to be created. + */ +typedef enum { + DROP_STATE_NORMAL, + DROP_STATE_NEW_BOOKMARK_FADING_IN, + DROP_STATE_NEW_BOOKMARK_ARMED, + DROP_STATE_NEW_BOOKMARK_FADING_OUT +} DropState; + struct _GtkPlacesSidebar { GtkScrolledWindow parent; @@ -143,6 +163,9 @@ struct _GtkPlacesSidebar { GtkPlacesOpenFlags open_flags; + DropState drop_state; + int new_bookmark_index; + guint show_desktop : 1; }; @@ -3588,6 +3611,9 @@ gtk_places_sidebar_init (GtkPlacesSidebar *sidebar) sidebar->hostnamed_cancellable, hostname_proxy_new_cb, g_object_ref (sidebar)); + + sidebar->drop_state = DROP_STATE_NORMAL; + sidebar->new_bookmark_index = -1; } static void From 624b352d776e8b2e4d62ab29d9a7d2afe736c9c4 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Fri, 19 Apr 2013 11:25:03 -0500 Subject: [PATCH 2/6] Move the drop feedback code to stub functions For now it is the old, simple call to gtk_tree_view_set_drag_dest_row(). We'll do something fancier in the following commits. Signed-off-by: Federico Mena Quintero --- gtk/gtkplacessidebar.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c index f66b7c05d8..1e47848a3d 100644 --- a/gtk/gtkplacessidebar.c +++ b/gtk/gtkplacessidebar.c @@ -1408,6 +1408,18 @@ free_drag_data (GtkPlacesSidebar *sidebar) } } +static void +start_drop_feedback (GtkPlacesSidebar *sidebar, GtkTreePath *path, GtkTreeViewDropPosition pos, gboolean drop_as_bookmarks) +{ + gtk_tree_view_set_drag_dest_row (sidebar->tree_view, path, pos); +} + +static void +stop_drop_feedback (GtkPlacesSidebar *sidebar) +{ + gtk_tree_view_set_drag_dest_row (sidebar->tree_view, NULL, 0); +} + static gboolean drag_motion_callback (GtkTreeView *tree_view, GdkDragContext *context, @@ -1421,8 +1433,10 @@ drag_motion_callback (GtkTreeView *tree_view, int action; GtkTreeIter iter; gboolean res; + gboolean drop_as_bookmarks; action = 0; + drop_as_bookmarks = FALSE; if (!sidebar->drag_data_received) { if (!get_drag_data (tree_view, context, time)) { @@ -1449,7 +1463,6 @@ drag_motion_callback (GtkTreeView *tree_view, if (sidebar->drag_list != NULL) { SectionType section_type; PlaceType place_type; - gboolean drop_as_bookmarks; gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store), &iter, path); @@ -1459,8 +1472,6 @@ drag_motion_callback (GtkTreeView *tree_view, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, -1); - drop_as_bookmarks = FALSE; - if (section_type == SECTION_BOOKMARKS) { if (pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER) { action = GDK_ACTION_COPY; @@ -1490,9 +1501,9 @@ drag_motion_callback (GtkTreeView *tree_view, out: if (action != 0) - gtk_tree_view_set_drag_dest_row (tree_view, path, pos); + start_drop_feedback (sidebar, path, pos, drop_as_bookmarks); else - gtk_tree_view_set_drag_dest_row (tree_view, NULL, pos); + stop_drop_feedback (sidebar); if (path != NULL) { gtk_tree_path_free (path); From bc0a9a33b0dfa323728590fe6bcbe0815db6d7a8 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Fri, 19 Apr 2013 15:03:17 -0500 Subject: [PATCH 3/6] Add a temporary row for feedback when inserting a bookmark is possible We'll prettify this gradually. Signed-off-by: Federico Mena Quintero --- gtk/gtkplacessidebar.c | 50 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c index 1e47848a3d..2be9b97f67 100644 --- a/gtk/gtkplacessidebar.c +++ b/gtk/gtkplacessidebar.c @@ -218,6 +218,7 @@ typedef enum { PLACES_MOUNTED_VOLUME, PLACES_BOOKMARK, PLACES_HEADING, + PLACES_DROP_FEEDBACK } PlaceType; typedef enum { @@ -1408,15 +1409,60 @@ free_drag_data (GtkPlacesSidebar *sidebar) } } +static void +remove_drop_bookmark_feedback_row (GtkPlacesSidebar *sidebar) +{ + if (sidebar->new_bookmark_index != -1) { + gboolean success; + GtkTreeIter iter; + + success = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (sidebar->store), &iter, NULL, sidebar->new_bookmark_index); + g_assert (success); + gtk_list_store_remove (sidebar->store, &iter); + sidebar->new_bookmark_index = -1; + } +} + static void start_drop_feedback (GtkPlacesSidebar *sidebar, GtkTreePath *path, GtkTreeViewDropPosition pos, gboolean drop_as_bookmarks) { - gtk_tree_view_set_drag_dest_row (sidebar->tree_view, path, pos); + if (drop_as_bookmarks) { + int new_bookmark_index; + GtkTreeIter iter; + + g_assert (pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER); + + new_bookmark_index = gtk_tree_path_get_indices (path)[0]; + + if (pos == GTK_TREE_VIEW_DROP_AFTER) + new_bookmark_index++; + + if (sidebar->new_bookmark_index != new_bookmark_index) { + GtkTreePath *new_path; + + remove_drop_bookmark_feedback_row (sidebar); + + /* Insert the new feedback row */ + sidebar->new_bookmark_index = new_bookmark_index; + gtk_list_store_insert_with_values (sidebar->store, &iter, sidebar->new_bookmark_index, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_DROP_FEEDBACK, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, SECTION_BOOKMARKS, + PLACES_SIDEBAR_COLUMN_NAME, _("New bookmark"), + PLACES_SIDEBAR_COLUMN_NO_EJECT, TRUE, + -1); + + new_path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter); + gtk_tree_view_set_drag_dest_row (sidebar->tree_view, new_path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); + gtk_tree_path_free (new_path); + } + } else + gtk_tree_view_set_drag_dest_row (sidebar->tree_view, path, pos); } static void stop_drop_feedback (GtkPlacesSidebar *sidebar) { + remove_drop_bookmark_feedback_row (sidebar); gtk_tree_view_set_drag_dest_row (sidebar->tree_view, NULL, 0); } @@ -1523,7 +1569,7 @@ drag_leave_callback (GtkTreeView *tree_view, GtkPlacesSidebar *sidebar) { free_drag_data (sidebar); - gtk_tree_view_set_drag_dest_row (tree_view, NULL, 0); + stop_drop_feedback (sidebar); g_signal_stop_emission_by_name (tree_view, "drag-leave"); } From 3815f5d9560c94481b95d24ddb31a8ddce3d213f Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Fri, 19 Apr 2013 15:49:54 -0500 Subject: [PATCH 4/6] Dropping on the feedback row is always possible Signed-off-by: Federico Mena Quintero --- gtk/gtkplacessidebar.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c index 2be9b97f67..697dad5cf7 100644 --- a/gtk/gtkplacessidebar.c +++ b/gtk/gtkplacessidebar.c @@ -1306,6 +1306,12 @@ compute_drop_position (GtkTreeView *tree_view, drop_possible = TRUE; + /* Normalize drops on the feedback row */ + if (place_type == PLACES_DROP_FEEDBACK) { + *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; + goto out; + } + /* Never drop on headings, but special case the bookmarks heading, * so we can drop bookmarks in between it and the first bookmark. */ @@ -1367,6 +1373,8 @@ compute_drop_position (GtkTreeView *tree_view, g_free (uri); } +out: + if (!drop_possible) { gtk_tree_path_free (*path); *path = NULL; @@ -1430,8 +1438,6 @@ start_drop_feedback (GtkPlacesSidebar *sidebar, GtkTreePath *path, GtkTreeViewDr int new_bookmark_index; GtkTreeIter iter; - g_assert (pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER); - new_bookmark_index = gtk_tree_path_get_indices (path)[0]; if (pos == GTK_TREE_VIEW_DROP_AFTER) @@ -1518,11 +1524,11 @@ drag_motion_callback (GtkTreeView *tree_view, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, -1); - if (section_type == SECTION_BOOKMARKS) { - if (pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER) { - action = GDK_ACTION_COPY; - drop_as_bookmarks = TRUE; - } + if (place_type == PLACES_DROP_FEEDBACK + || (section_type == SECTION_BOOKMARKS + && (pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER))) { + action = GDK_ACTION_COPY; + drop_as_bookmarks = TRUE; } if (!drop_as_bookmarks) { From a1b9bf7fbc9da6b162327c59ee2d347f1b16a033 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Fri, 19 Apr 2013 17:22:39 -0500 Subject: [PATCH 5/6] Adjust the drag destination row when it is above the currently highlighted row This makes the feedback accurate and without hysteresis. Haven't I written this code ten times before? Signed-off-by: Federico Mena Quintero --- gtk/gtkplacessidebar.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c index 697dad5cf7..7982e0f9c3 100644 --- a/gtk/gtkplacessidebar.c +++ b/gtk/gtkplacessidebar.c @@ -1431,12 +1431,35 @@ remove_drop_bookmark_feedback_row (GtkPlacesSidebar *sidebar) } } +#if 0 +static const char * +pos_to_string (GtkTreeViewDropPosition pos) +{ + switch (pos) { + case GTK_TREE_VIEW_DROP_BEFORE: + return "before"; + + case GTK_TREE_VIEW_DROP_AFTER: + return "after"; + + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + return "into_or_before"; + + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + return "into_or_after"; + + default: + return "w00t"; + } +} +#endif + static void start_drop_feedback (GtkPlacesSidebar *sidebar, GtkTreePath *path, GtkTreeViewDropPosition pos, gboolean drop_as_bookmarks) { if (drop_as_bookmarks) { int new_bookmark_index; - GtkTreeIter iter; + GtkTreePath *new_path; new_bookmark_index = gtk_tree_path_get_indices (path)[0]; @@ -1444,7 +1467,11 @@ start_drop_feedback (GtkPlacesSidebar *sidebar, GtkTreePath *path, GtkTreeViewDr new_bookmark_index++; if (sidebar->new_bookmark_index != new_bookmark_index) { - GtkTreePath *new_path; + GtkTreeIter iter; + + if (sidebar->new_bookmark_index != -1 + && sidebar->new_bookmark_index < new_bookmark_index) + new_bookmark_index--; /* since the removal of the old feedback row pushed items one position up */ remove_drop_bookmark_feedback_row (sidebar); @@ -1456,11 +1483,11 @@ start_drop_feedback (GtkPlacesSidebar *sidebar, GtkTreePath *path, GtkTreeViewDr PLACES_SIDEBAR_COLUMN_NAME, _("New bookmark"), PLACES_SIDEBAR_COLUMN_NO_EJECT, TRUE, -1); - - new_path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter); - gtk_tree_view_set_drag_dest_row (sidebar->tree_view, new_path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); - gtk_tree_path_free (new_path); } + + new_path = gtk_tree_path_new_from_indices (new_bookmark_index, -1); + gtk_tree_view_set_drag_dest_row (sidebar->tree_view, new_path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); + gtk_tree_path_free (new_path); } else gtk_tree_view_set_drag_dest_row (sidebar->tree_view, path, pos); } @@ -1468,7 +1495,6 @@ start_drop_feedback (GtkPlacesSidebar *sidebar, GtkTreePath *path, GtkTreeViewDr static void stop_drop_feedback (GtkPlacesSidebar *sidebar) { - remove_drop_bookmark_feedback_row (sidebar); gtk_tree_view_set_drag_dest_row (sidebar->tree_view, NULL, 0); } @@ -1576,6 +1602,7 @@ drag_leave_callback (GtkTreeView *tree_view, { free_drag_data (sidebar); stop_drop_feedback (sidebar); + remove_drop_bookmark_feedback_row (sidebar); g_signal_stop_emission_by_name (tree_view, "drag-leave"); } From 2db8e25cdb2aab8de12e88b9532f322c2fb56416 Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Fri, 19 Apr 2013 17:20:12 -0500 Subject: [PATCH 6/6] treeview: Use equal areas in the computation of drag dest row positions We used to divide the row in thirds vertically, and use the outer thirds for GTK_TREE_VIEW_DROP_BEFORE and AFTER, respectively. Now we use *fourths*. This is so that we get equal areas for these: GTK_TREE_VIEW_DROP_BEFORE GTK_TREE_VIEW_DROP_INTO_OR_BEFORE GTK_TREE_VIEW_DROP_INTO_OR_AFTER GTK_TREE_VIEW_DROP_AFTER This makes hovering tree rows much more positive. Signed-off-by: Federico Mena Quintero --- gtk/gtktreeview.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index cb43c7f2d0..3de5599bec 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -14420,7 +14420,7 @@ gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, gint cell_y; gint bin_x, bin_y; gdouble offset_into_row; - gdouble third; + gdouble fourth; GdkRectangle cell; GtkTreeViewColumn *column = NULL; GtkTreePath *tmp_path = NULL; @@ -14442,8 +14442,8 @@ gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, if (tree_view->priv->tree == NULL) return FALSE; - /* If in the top third of a row, we drop before that row; if - * in the bottom third, drop after that row; if in the middle, + /* If in the top fourth of a row, we drop before that row; if + * in the bottom fourth, drop after that row; if in the middle, * and the row has children, drop into the row. */ gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y, @@ -14470,11 +14470,11 @@ gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, tmp_path = NULL; - third = cell.height / 3.0; + fourth = cell.height / 4.0; if (pos) { - if (offset_into_row < third) + if (offset_into_row < fourth) { *pos = GTK_TREE_VIEW_DROP_BEFORE; } @@ -14482,7 +14482,7 @@ gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, { *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; } - else if (offset_into_row < third * 2.0) + else if (offset_into_row < cell.height - fourth) { *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER; }