Skip to content

Commit 1e84f0b

Browse files
committed
Add rubber-band selection to the list view.
In order to keep some sanity between this and item dragging, the following rules are in place: - Rubber-banding can only begin when an item is first being selected. Clicking the same row a second time and dragging will initiate a drag as before. - Rubber banding can only start in non-text/non-icon portions of a row. Clicking and dragging over non-blank regions will initiate a drag as before. Fixes #416
1 parent fc1194a commit 1e84f0b

1 file changed

Lines changed: 70 additions & 22 deletions

File tree

src/nemo-list-view.c

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ struct NemoListViewDetails {
9898
gboolean menus_ready;
9999
gboolean active;
100100

101+
gboolean rubber_banding;
102+
101103
GHashTable *columns;
102104
GtkWidget *column_editor;
103105

@@ -581,9 +583,9 @@ motion_notify_callback (GtkWidget *widget,
581583

582584
view = NEMO_LIST_VIEW (callback_data);
583585

584-
if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget))) {
585-
return FALSE;
586-
}
586+
if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget))) {
587+
return GDK_EVENT_PROPAGATE;
588+
}
587589

588590
if (get_click_policy () == NEMO_CLICK_POLICY_SINGLE) {
589591
GtkTreePath *old_hover_path;
@@ -607,27 +609,68 @@ motion_notify_callback (GtkWidget *widget,
607609
}
608610
}
609611

610-
if (view->details->drag_button != 0) {
611-
if (!source_target_list) {
612-
source_target_list = nemo_list_model_get_drag_target_list ();
613-
}
612+
/* If we're already rubber-banding, we can skip all of this logic and just let the parent
613+
* class continue to handle selection */
614+
if (view->details->drag_button != 0 && !view->details->rubber_banding) {
615+
GtkTreePath *path;
616+
GtkTreeSelection *selection;
617+
gboolean is_new_self_selection;
614618

615-
if (gtk_drag_check_threshold (widget,
616-
view->details->drag_x,
617-
view->details->drag_y,
618-
event->x,
619-
event->y)) {
620-
gtk_drag_begin
621-
(widget,
622-
source_target_list,
623-
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK,
624-
view->details->drag_button,
625-
(GdkEvent*)event);
626-
}
627-
return TRUE;
628-
}
619+
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
629620

630-
return FALSE;
621+
gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
622+
event->x, event->y,
623+
&path,
624+
NULL, NULL, NULL);
625+
626+
/* This looks complicated but it's just verbose: We'll only consider allowing rubber-banding
627+
* to begin if the following are TRUE: a) The current row is the only row currently selected,
628+
* and b) This is the first click that's been made on this row - meaning, the button-press-event
629+
* that preceded this motion-event was the one that caused this row to be selected. */
630+
is_new_self_selection = gtk_tree_selection_count_selected_rows (selection) == 1 &&
631+
gtk_tree_selection_path_is_selected (selection, path) &&
632+
(!view->details->double_click_path[1] ||
633+
(view->details->double_click_path[1] &&
634+
gtk_tree_path_compare (view->details->double_click_path[0],
635+
view->details->double_click_path[1]) != 0));
636+
637+
gtk_tree_path_free (path);
638+
639+
/* We also want to further restrict rubber-banding to be initiated only in blank areas of the row.
640+
* This allows DnD to operate on a new selection like before, when the motion begins over text or
641+
* icons */
642+
if (is_new_self_selection && gtk_tree_view_is_blank_at_pos (GTK_TREE_VIEW (widget),
643+
event->x, event->y,
644+
NULL, NULL, NULL, NULL)) {
645+
/* If this is a candidate for rubber-banding, track that state in the view, and allow the event
646+
* to continue into Gtk (which handles rubber-band selection for us) */
647+
view->details->rubber_banding = TRUE;
648+
649+
return GDK_EVENT_PROPAGATE;
650+
}
651+
652+
/* All other cases, allow DnD to potentially begin */
653+
if (!source_target_list) {
654+
source_target_list = nemo_list_model_get_drag_target_list ();
655+
}
656+
657+
if (gtk_drag_check_threshold (widget,
658+
view->details->drag_x,
659+
view->details->drag_y,
660+
event->x,
661+
event->y)) {
662+
gtk_drag_begin (widget,
663+
source_target_list,
664+
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK,
665+
view->details->drag_button,
666+
(GdkEvent*) event);
667+
}
668+
669+
/* The event is handled by the DnD begin, don't propagate further */
670+
return GDK_EVENT_STOP;
671+
}
672+
673+
return GDK_EVENT_PROPAGATE;
631674
}
632675

633676
static gboolean
@@ -1177,6 +1220,8 @@ button_release_callback (GtkWidget *widget,
11771220

11781221
view = NEMO_LIST_VIEW (callback_data);
11791222

1223+
view->details->rubber_banding = FALSE;
1224+
11801225
if (event->button == view->details->drag_button) {
11811226
stop_drag_check (view);
11821227
if (!view->details->drag_started &&
@@ -2103,6 +2148,9 @@ create_and_set_up_tree_view (NemoListView *view)
21032148
gchar **default_column_order, **default_visible_columns;
21042149

21052150
view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
2151+
2152+
gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (view->details->tree_view), TRUE);
2153+
21062154
view->details->columns = g_hash_table_new_full (g_str_hash,
21072155
g_str_equal,
21082156
(GDestroyNotify) g_free,

0 commit comments

Comments
 (0)