Merge lp:~jeremywootten/pantheon-files/fix-1399674-linear-selection-in-icon-view into lp:~elementary-apps/pantheon-files/trunk

Proposed by Jeremy Wootten
Status: Merged
Approved by: Cody Garver
Approved revision: 2050
Merged at revision: 2053
Proposed branch: lp:~jeremywootten/pantheon-files/fix-1399674-linear-selection-in-icon-view
Merge into: lp:~elementary-apps/pantheon-files/trunk
Diff against target: 321 lines (+215/-8)
3 files modified
src/View/AbstractDirectoryView.vala (+78/-8)
src/View/AbstractTreeView.vala (+2/-0)
src/View/IconView.vala (+135/-0)
To merge this branch: bzr merge lp:~jeremywootten/pantheon-files/fix-1399674-linear-selection-in-icon-view
Reviewer Review Type Date Requested Status
elementary Apps team Pending
Review via email: mp+283740@code.launchpad.net

Commit message

Implement linear selection with Shift-Click in IconView (lp:1399674)

Description of the change

This branch overrides the native Shift-Click behaviour of Gtk.IconView to provide a behaviour similar to that of ListView and ColumnView.

To post a comment you must log in.
2050. By Jeremy Wootten

Enable linear selection with cursor keys; refine linear selection to match ListView more closely

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/View/AbstractDirectoryView.vala'
--- src/View/AbstractDirectoryView.vala 2016-01-23 12:37:18 +0000
+++ src/View/AbstractDirectoryView.vala 2016-02-06 15:43:57 +0000
@@ -201,6 +201,10 @@
201 gtk_tree_model_get(): this function increases the reference201 gtk_tree_model_get(): this function increases the reference
202 count of the file object.*/202 count of the file object.*/
203 protected GLib.List<GOF.File> selected_files = null;203 protected GLib.List<GOF.File> selected_files = null;
204 /* support for linear selection mode in icon view */
205 protected bool previous_selection_was_linear = false;
206 protected Gtk.TreePath? previous_linear_selection_path = null;
207 protected int previous_linear_selection_direction = 0;
204208
205 private GLib.List<GLib.File> templates = null;209 private GLib.List<GLib.File> templates = null;
206210
@@ -2533,6 +2537,19 @@
2533 bool in_trash = slot.location.has_uri_scheme ("trash");2537 bool in_trash = slot.location.has_uri_scheme ("trash");
2534 bool in_recent = slot.location.has_uri_scheme ("recent");2538 bool in_recent = slot.location.has_uri_scheme ("recent");
25352539
2540
2541 /* Implement linear selection in Icon View with cursor keys */
2542 bool linear_select_required = false;
2543 if (!no_mods && !only_control_pressed) {
2544 if (only_shift_pressed && (this is IconView)) {
2545 linear_select_required = true;
2546 } else {
2547 previous_selection_was_linear = false;
2548 }
2549 } else {
2550 previous_selection_was_linear = false;
2551 }
2552
2536 switch (event.keyval) {2553 switch (event.keyval) {
2537 case Gdk.Key.F10:2554 case Gdk.Key.F10:
2538 if (only_control_pressed) {2555 if (only_control_pressed) {
@@ -2628,6 +2645,32 @@
26282645
2629 return true;2646 return true;
26302647
2648 case Gdk.Key.Up:
2649 case Gdk.Key.Down:
2650 case Gdk.Key.Left:
2651 case Gdk.Key.Right:
2652
2653 if (linear_select_required && selected_files.length () > 0) { /* Only true for Icon View */
2654 Gtk.TreePath? path = get_path_at_cursor ();
2655 if (path != null) {
2656 if (event.keyval == Gdk.Key.Right) {
2657 path.next ();
2658 } else if (event.keyval == Gdk.Key.Left) {
2659 path.prev ();
2660 } else if (event.keyval == Gdk.Key.Up) {
2661 path = up (path);
2662 } else if (event.keyval == Gdk.Key.Down) {
2663 path = down (path);
2664 }
2665 linear_select_path (path);
2666 return true;
2667 }
2668 } else {
2669 previous_selection_was_linear = false;
2670 previous_linear_selection_path = null;
2671 }
2672 break;
2673
2631 default:2674 default:
2632 break;2675 break;
2633 }2676 }
@@ -2938,11 +2981,13 @@
2938 var mods = event.state & Gtk.accelerator_get_default_mod_mask ();2981 var mods = event.state & Gtk.accelerator_get_default_mod_mask ();
2939 bool no_mods = (mods == 0);2982 bool no_mods = (mods == 0);
2940 bool control_pressed = ((mods & Gdk.ModifierType.CONTROL_MASK) != 0);2983 bool control_pressed = ((mods & Gdk.ModifierType.CONTROL_MASK) != 0);
2984 bool shift_pressed = ((mods & Gdk.ModifierType.SHIFT_MASK) != 0);
2941 bool other_mod_pressed = (((mods & ~Gdk.ModifierType.SHIFT_MASK) & ~Gdk.ModifierType.CONTROL_MASK) != 0);2985 bool other_mod_pressed = (((mods & ~Gdk.ModifierType.SHIFT_MASK) & ~Gdk.ModifierType.CONTROL_MASK) != 0);
2942 bool only_control_pressed = control_pressed && !other_mod_pressed; /* Shift can be pressed */2986 bool only_control_pressed = control_pressed && !other_mod_pressed; /* Shift can be pressed */
29432987 bool only_shift_pressed = shift_pressed && !control_pressed && !other_mod_pressed;
2944 bool path_selected = (path != null ? path_is_selected (path) : false);2988 bool path_selected = (path != null ? path_is_selected (path) : false);
2945 bool on_blank = (click_zone == ClickZone.BLANK_NO_PATH || click_zone == ClickZone.BLANK_PATH);2989 bool on_blank = (click_zone == ClickZone.BLANK_NO_PATH || click_zone == ClickZone.BLANK_PATH);
2990 bool linear_select_required = false;
29462991
2947 /* Block drag and drop to allow rubberbanding and prevent unwanted effects of2992 /* Block drag and drop to allow rubberbanding and prevent unwanted effects of
2948 * dragging on blank areas2993 * dragging on blank areas
@@ -2951,8 +2996,16 @@
29512996
2952 /* Handle un-modified clicks or control-clicks here else pass on.2997 /* Handle un-modified clicks or control-clicks here else pass on.
2953 */2998 */
2999 linear_select_required = false;
2954 if (!no_mods && !only_control_pressed) {3000 if (!no_mods && !only_control_pressed) {
2955 return window.button_press_event (event);3001 if (only_shift_pressed && (this is IconView)) {
3002 linear_select_required = true;
3003 } else {
3004 previous_selection_was_linear = false;
3005 return window.button_press_event (event);
3006 }
3007 } else {
3008 previous_selection_was_linear = false;
2956 }3009 }
29573010
2958 if (!path_selected && click_zone != ClickZone.HELPER) {3011 if (!path_selected && click_zone != ClickZone.HELPER) {
@@ -2994,17 +3047,31 @@
2994 */3047 */
29953048
2996 if (!no_mods || (on_blank && (!activate_on_blank || !path_selected)))3049 if (!no_mods || (on_blank && (!activate_on_blank || !path_selected)))
2997 result = false; /* Rubberband */3050 if (linear_select_required && selected_files.length () > 0) {
3051 linear_select_path (path);
3052 } else {
3053 previous_selection_was_linear = false;
3054 result = false; /* Rubberband */
3055 }
2998 else3056 else
2999 result = handle_primary_button_click (event, path);3057 result = handle_primary_button_click (event, path);
30003058
3059 previous_linear_selection_path = path.copy ();
3001 break;3060 break;
30023061
3003 case ClickZone.HELPER:3062 case ClickZone.HELPER:
3004 if (path_selected)3063 if (linear_select_required && selected_files.length () > 0) {
3005 unselect_path (path);3064 linear_select_path (path);
3006 else3065 } else {
3007 select_path (path);3066 previous_selection_was_linear = false;
3067 previous_linear_selection_path = null;
3068
3069 if (path_selected) {
3070 unselect_path (path);
3071 } else {
3072 select_path (path);
3073 }
3074 }
30083075
3009 break;3076 break;
30103077
@@ -3046,7 +3113,7 @@
3046 result = handle_default_button_click (event);3113 result = handle_default_button_click (event);
3047 break;3114 break;
3048 }3115 }
30493116 previous_linear_selection_path = path != null ? path.copy () : null;
3050 return result;3117 return result;
3051 }3118 }
30523119
@@ -3296,6 +3363,9 @@
32963363
3297 public virtual void sync_selection () {}3364 public virtual void sync_selection () {}
3298 public virtual void highlight_path (Gtk.TreePath? path) {}3365 public virtual void highlight_path (Gtk.TreePath? path) {}
3366 protected virtual void linear_select_path (Gtk.TreePath path) {}
3367 protected virtual Gtk.TreePath up (Gtk.TreePath path) {path.up (); return path;}
3368 protected virtual Gtk.TreePath down (Gtk.TreePath path) {path.down (); return path;}
32993369
3300/** Abstract methods - must be overridden*/3370/** Abstract methods - must be overridden*/
3301 public abstract GLib.List<Gtk.TreePath> get_selected_paths () ;3371 public abstract GLib.List<Gtk.TreePath> get_selected_paths () ;
33023372
=== modified file 'src/View/AbstractTreeView.vala'
--- src/View/AbstractTreeView.vala 2015-10-06 14:23:33 +0000
+++ src/View/AbstractTreeView.vala 2016-02-06 15:43:57 +0000
@@ -133,6 +133,8 @@
133 public override void select_path (Gtk.TreePath? path) {133 public override void select_path (Gtk.TreePath? path) {
134 if (path != null) {134 if (path != null) {
135 debug ("select path %s", path.to_string ());135 debug ("select path %s", path.to_string ());
136 /* Ensure cursor follows last selection */
137 tree.set_cursor (path, null, false);
136 tree.get_selection ().select_path (path);138 tree.get_selection ().select_path (path);
137 }139 }
138 }140 }
139141
=== modified file 'src/View/IconView.vala'
--- src/View/IconView.vala 2016-01-15 19:53:37 +0000
+++ src/View/IconView.vala 2016-02-06 15:43:57 +0000
@@ -142,6 +142,8 @@
142142
143 public override void select_path (Gtk.TreePath? path) {143 public override void select_path (Gtk.TreePath? path) {
144 if (path != null) {144 if (path != null) {
145 /* Ensure cursor follows last selection */
146 tree.set_cursor (path, null, false);
145 tree.select_path (path);147 tree.select_path (path);
146 }148 }
147 }149 }
@@ -311,5 +313,138 @@
311 tree_frozen = false;313 tree_frozen = false;
312 }314 }
313 }315 }
316
317 protected override void linear_select_path (Gtk.TreePath path) {
318 /* We override the native Gtk.IconView behaviour when selecting files with Shift-Click */
319 /* We wish to emulate the behaviour of ListView and ColumnView. This depends on whether the */
320 /* the previous selection was made with the Shift key pressed */
321 /* Note: 'first' and 'last' refer to position in selection, not the time selected */
322
323 if (path == null) {
324 critical ("Ignoring attempt to select null path in linear_select_path");
325 return;
326 }
327 if (previous_linear_selection_path != null && path.compare (previous_linear_selection_path) == 0) {
328 /* Ignore if repeat click on same file as before. We keep the previous linear selection direction. */
329 return;
330 }
331
332 var selected_paths = tree.get_selected_items ();
333 /* Ensure the order of the selected files list matches the visible order */
334 selected_paths.sort (Gtk.TreePath.compare);
335
336 var first_selected = selected_paths.first ().data;
337 var last_selected = selected_paths.last ().data;
338 bool before_first = path.compare (first_selected) <= 0;
339 bool after_last = path.compare (last_selected) >= 0;
340 bool direction_change = false;
341
342 direction_change = (before_first && previous_linear_selection_direction > 0) ||
343 (after_last && previous_linear_selection_direction < 0);
344
345 var p = path.copy ();
346 Gtk.TreePath p2 = null;
347
348 unselect_all ();
349 Gtk.TreePath? end_path = null;
350 if (!previous_selection_was_linear && previous_linear_selection_path != null) {
351 end_path = previous_linear_selection_path;
352 } else if (before_first) {
353 end_path = direction_change ? first_selected : last_selected;
354 } else {
355 end_path = direction_change ? last_selected : first_selected;
356 }
357
358 if (before_first) {
359 do {
360 p2 = p.copy ();
361 select_path (p);
362 p.next ();
363 } while (p.compare (p2) != 0 && p.compare (end_path) <= 0);
364 } else if (after_last) {
365 do {
366 select_path (p);
367 p2 = p.copy ();
368 p.prev ();
369 } while (p.compare (p2) != 0 && p.compare (end_path) >= 0);
370 } else {/* between first and last */
371 do {
372 p2 = p.copy ();
373 select_path (p);
374 p.prev ();
375 } while (p.compare (p2) != 0 && p.compare (first_selected) >= 0);
376
377 p = path.copy ();
378 do {
379 p2 = p.copy ();
380 p.next ();
381 unselect_path (p);
382 } while (p.compare (p2) != 0 && p.compare (last_selected) <= 0);
383 }
384 previous_selection_was_linear = true;
385
386 selected_paths = tree.get_selected_items ();
387 selected_paths.sort (Gtk.TreePath.compare);
388
389 first_selected = selected_paths.first ().data;
390 last_selected = selected_paths.last ().data;
391
392 if (path.compare (last_selected) == 0) {
393 previous_linear_selection_direction = 1; /* clicked after the (visually) first selection */
394 } else if (path.compare (first_selected) == 0) {
395 previous_linear_selection_direction = -1; /* clicked before the (visually) first selection */
396 } else {
397 critical ("Linear selection did not become end point - this should not happen!");
398 previous_linear_selection_direction = 0;
399 }
400 previous_linear_selection_path = path.copy ();
401 /* Ensure cursor in correct place, regardless of any selections made in this function */
402 tree.set_cursor (path, null, false);
403 tree.scroll_to_path (path, false, 0.5f, 0.5f);
404 }
405
406 protected override Gtk.TreePath up (Gtk.TreePath path) {
407 int item_row = tree.get_item_row (path);
408 if (item_row == 0) {
409 return path;
410 }
411 int cols = get_n_cols ();
412 int index = path.get_indices ()[0];
413 Gtk.TreePath new_path;
414 Gtk.TreeIter? iter = null;
415 new_path = new Gtk.TreePath.from_indices (index - cols, -1);
416 if (tree.model.get_iter (out iter, new_path)) {
417 return new_path;
418 } else {
419 return path;
420 }
421 }
422 protected override Gtk.TreePath down (Gtk.TreePath path) {
423 int cols = get_n_cols ();
424 int index = path.get_indices ()[0];
425
426 Gtk.TreePath new_path;
427 Gtk.TreeIter? iter = null;
428 new_path = new Gtk.TreePath.from_indices (index + cols, -1);
429 if (tree.model.get_iter (out iter, new_path)) {
430 return new_path;
431 } else {
432 return path;
433 }
434 }
435
436 /* When Icon View is automatically adjusting column number it does not expose the actual number of
437 * columns (get_columns () returns -1). So we have to write our own method. This is the only way
438 * (I can think of) that works on row 0.
439 */
440 private int get_n_cols () {
441 var path = new Gtk.TreePath.from_indices (0, -1);
442 int index = 0;
443 while (tree.get_item_row (path) == 0) {
444 index++;
445 path.next ();
446 }
447 return index;
448 }
314 }449 }
315}450}

Subscribers

People subscribed via source and target branches

to all changes: