Merge lp:~jeremywootten/pantheon-files/fix-hang-on-large-copy into lp:~elementary-apps/pantheon-files/trunk
- fix-hang-on-large-copy
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Danielle Foré |
Approved revision: | 2374 |
Merged at revision: | 2574 |
Proposed branch: | lp:~jeremywootten/pantheon-files/fix-hang-on-large-copy |
Merge into: | lp:~elementary-apps/pantheon-files/trunk |
Diff against target: |
383 lines (+110/-63) 4 files modified
src/View/AbstractDirectoryView.vala (+62/-31) src/View/AbstractTreeView.vala (+26/-18) src/View/IconView.vala (+21/-13) src/View/Slot.vala (+1/-1) |
To merge this branch: | bzr merge lp:~jeremywootten/pantheon-files/fix-hang-on-large-copy |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Adam Bieńkowski (community) | testing | Approve | |
Dieter Debast (community) | ux | Approve | |
Zisu Andrei | Pending | ||
Review via email: mp+314546@code.launchpad.net |
This proposal supersedes a proposal from 2016-11-04.
Commit message
Fix hang on selecting many files after paste or view change.
Description of the change
This fixes the exponentially increasing time take to select the debuting files in the view after pasting. With a large number of files (e.g. 1000+) the interface appears to hang when using List View.
TO TEST:
A.
IN ICON VIEW
1) Create two test folders and open each in a tab
2) In one folder create a large number (~1000) of small files (~1K)
3) Select all the files (Ctrl-A)
4) Copy the files to the clipboard
5) Switch to empty folder and paste the files.
In trunk the interface appears to hang for a long time after the pasted files appear (A core is maxed out).
B. IN LIST VIEW
1) Create many files as described under A.
2) Select them all.
3) Switch to icon view.
In trunk the interface appears to hang for a long time while the selected files are re-selected (one at a time) in the new view (A core is maxed out).
KILL THE PANTHEON-FILES PROCESS BEFORE RUNNING THIS BRANCH.
In this branch the pasted files or reselected files are all selected soon after they appear. The interface does not hang.
The branch additionally fixes pasting into a List or Column View - all the pasted files are selected (trunk only selects the one file, unlike Icon View)
The same code is now used whether pasting is done via keyboard or drag and drop (simplification).
Zisu Andrei (matzipan) wrote : Posted in a previous version of this proposal | # |
Zisu Andrei (matzipan) wrote : Posted in a previous version of this proposal | # |
Code also LGTM.
Not merging this yet. Waiting on merging okay from Cody.
Zisu Andrei (matzipan) wrote : Posted in a previous version of this proposal | # |
Scratch that. I'm seeing a very nasty bug. I thought it was in trunk too but it isn't.
My folder structure is like this:
folder 1
2000 text files
If I am viewing this folder and I press ctrl+a, I then deselect "folder 1", I then click "folder 1" to enter it. This results in opening all 2000 files in Scratch.
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal | # |
Thanks for spotting that bad regression. The latest version fixes that and also simplifies the code a little. Both pasted and dropped files are selected but it was being done in different ways for some reason. Now pasted files are selected in the same way as dropped files.
Zisu Andrei (matzipan) wrote : Posted in a previous version of this proposal | # |
I can't seem to be noticing any significant improvement (maybe I was mistaken earlier). I made sure to kill all the processes, double checked the versions I have installed, etc.
I tried both over an SSD and an HDD.
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal | # |
The problem does not occur if you use List or Column view (at least not with the operations mentioned in TESTING). Only if both views are Icon View. This is because only one of the pasted files is selected in List View, but they are all selected in Icon View (which is a bug in itself - the behaviour should be consistent).
You should be able to reproduce the bug using only one tab using the second set of instructions now in the description.
Jeremy Wootten (jeremywootten) wrote : Posted in a previous version of this proposal | # |
Testing instructions corrected and extended.
Dieter Debast (ddieter) wrote : | # |
I can confirm that it works much smoother on this branch.
Adam Bieńkowski (donadigo) wrote : | # |
This works properly here however I cannot verify if the changes in the code make sense because of Files code complexity, anyway I'm giving this an approve.
RabbitBot (rabbitbot-a) wrote : | # |
Attempt to merge into lp:pantheon-files failed due to conflicts:
text conflict in src/View/
Jeremy Wootten (jeremywootten) wrote : | # |
Conflict with trunk r2572 resolved.
RabbitBot (rabbitbot-a) wrote : | # |
Attempt to merge into lp:pantheon-files failed due to conflicts:
text conflict in src/View/
- 2374. By Jeremy Wootten
-
Merge trunk to r2573 and resolve conflict in AbstractDirecto
ryView
Jeremy Wootten (jeremywootten) wrote : | # |
Conflict with trunk r2573 resolved.
Preview Diff
1 | === modified file 'src/View/AbstractDirectoryView.vala' | |||
2 | --- src/View/AbstractDirectoryView.vala 2017-06-05 20:25:37 +0000 | |||
3 | +++ src/View/AbstractDirectoryView.vala 2017-06-06 09:01:03 +0000 | |||
4 | @@ -239,12 +239,20 @@ | |||
5 | 239 | action_set_enabled (common_actions, "paste_into", false); | 239 | action_set_enabled (common_actions, "paste_into", false); |
6 | 240 | action_set_enabled (window.win_actions, "select_all", false); | 240 | action_set_enabled (window.win_actions, "select_all", false); |
7 | 241 | 241 | ||
8 | 242 | /* Fix problems when navigating away from directory with large number | ||
9 | 243 | * of selected files (e.g. OverlayBar critical errors) | ||
10 | 244 | */ | ||
11 | 245 | disconnect_tree_signals (); | ||
12 | 246 | |||
13 | 242 | size_allocate.disconnect (on_size_allocate); | 247 | size_allocate.disconnect (on_size_allocate); |
14 | 243 | clipboard.changed.disconnect (on_clipboard_changed); | 248 | clipboard.changed.disconnect (on_clipboard_changed); |
15 | 244 | view.enter_notify_event.disconnect (on_enter_notify_event); | 249 | view.enter_notify_event.disconnect (on_enter_notify_event); |
16 | 245 | view.key_press_event.disconnect (on_view_key_press_event); | 250 | view.key_press_event.disconnect (on_view_key_press_event); |
17 | 246 | } else if (!value && _is_frozen) { | 251 | } else if (!value && _is_frozen) { |
19 | 247 | update_menu_actions (); | 252 | /* Ensure selected files and menu actions are up to date */ |
20 | 253 | connect_tree_signals (); | ||
21 | 254 | on_view_selection_changed (); | ||
22 | 255 | |||
23 | 248 | size_allocate.connect (on_size_allocate); | 256 | size_allocate.connect (on_size_allocate); |
24 | 249 | clipboard.changed.connect (on_clipboard_changed); | 257 | clipboard.changed.connect (on_clipboard_changed); |
25 | 250 | view.enter_notify_event.connect (on_enter_notify_event); | 258 | view.enter_notify_event.connect (on_enter_notify_event); |
26 | @@ -427,37 +435,45 @@ | |||
27 | 427 | }); | 435 | }); |
28 | 428 | } | 436 | } |
29 | 429 | } | 437 | } |
32 | 430 | public void select_glib_files (GLib.List<GLib.File> location_list, GLib.File? focus_location) { | 438 | |
33 | 431 | unselect_all (); | 439 | public void select_glib_files_when_thawed (GLib.List<GLib.File> location_list, GLib.File? focus_location) { |
34 | 432 | GLib.List<GOF.File>? file_list = null; | 440 | GLib.List<GOF.File>? file_list = null; |
35 | 433 | 441 | ||
36 | 434 | location_list.@foreach ((loc) => { | 442 | location_list.@foreach ((loc) => { |
37 | 435 | file_list.prepend (GOF.File.@get (loc)); | 443 | file_list.prepend (GOF.File.@get (loc)); |
38 | 436 | }); | 444 | }); |
39 | 437 | 445 | ||
40 | 446 | GLib.File? focus = focus_location != null ? focus_location.dup () : null; | ||
41 | 447 | |||
42 | 438 | /* Because the Icon View disconnects the model while loading, we need to wait until | 448 | /* Because the Icon View disconnects the model while loading, we need to wait until |
43 | 439 | * the tree is thawed and the model reconnected before selecting the files */ | 449 | * the tree is thawed and the model reconnected before selecting the files */ |
44 | 440 | Idle.add_full (GLib.Priority.LOW, () => { | 450 | Idle.add_full (GLib.Priority.LOW, () => { |
45 | 441 | if (!tree_frozen) { | 451 | if (!tree_frozen) { |
59 | 442 | file_list.@foreach ((file) => { | 452 | select_file_paths (file_list, focus); |
47 | 443 | Gtk.TreeIter iter; | ||
48 | 444 | if (model.get_first_iter_for_file (file, out iter)) { | ||
49 | 445 | Gtk.TreePath path = model.get_path (iter); | ||
50 | 446 | if (path != null) { | ||
51 | 447 | select_path (path); | ||
52 | 448 | if (focus_location != null && focus_location.equal (file.location)) { | ||
53 | 449 | /* set cursor and scroll to focus location*/ | ||
54 | 450 | set_cursor (path, false, false, true); | ||
55 | 451 | } | ||
56 | 452 | } | ||
57 | 453 | } | ||
58 | 454 | }); | ||
60 | 455 | return false; | 453 | return false; |
62 | 456 | } else | 454 | } else { |
63 | 457 | return true; | 455 | return true; |
64 | 456 | } | ||
65 | 458 | }); | 457 | }); |
66 | 459 | } | 458 | } |
67 | 460 | 459 | ||
68 | 460 | private void select_file_paths (GLib.List<GOF.File> files, GLib.File? focus) { | ||
69 | 461 | |||
70 | 462 | Gtk.TreeIter iter; | ||
71 | 463 | disconnect_tree_signals (); /* Avoid unnecessary signal processing */ | ||
72 | 464 | unselect_all (); | ||
73 | 465 | |||
74 | 466 | foreach (GOF.File f in files) { | ||
75 | 467 | if (model.get_first_iter_for_file (f, out iter)) { | ||
76 | 468 | var path = model.get_path (iter); | ||
77 | 469 | select_path (path, focus != null && focus.equal (f.location)); /* Cursor follows if matches focus location*/ | ||
78 | 470 | } | ||
79 | 471 | } | ||
80 | 472 | |||
81 | 473 | connect_tree_signals (); | ||
82 | 474 | on_view_selection_changed (); /* Update selected files and menu actions */ | ||
83 | 475 | } | ||
84 | 476 | |||
85 | 461 | public unowned GLib.List<GLib.AppInfo> get_open_with_apps () { | 477 | public unowned GLib.List<GLib.AppInfo> get_open_with_apps () { |
86 | 462 | return open_with_apps; | 478 | return open_with_apps; |
87 | 463 | } | 479 | } |
88 | @@ -587,7 +603,7 @@ | |||
89 | 587 | return; /* file not in model */ | 603 | return; /* file not in model */ |
90 | 588 | 604 | ||
91 | 589 | var path = model.get_path (iter); | 605 | var path = model.get_path (iter); |
93 | 590 | select_path (path); | 606 | select_path (path); /* Cursor does not follow */ |
94 | 591 | } | 607 | } |
95 | 592 | 608 | ||
96 | 593 | /** Directory signal handlers. */ | 609 | /** Directory signal handlers. */ |
97 | @@ -1203,6 +1219,7 @@ | |||
98 | 1203 | clipboard.copy_files (get_selected_files_for_transfer (get_files_for_action ())); | 1219 | clipboard.copy_files (get_selected_files_for_transfer (get_files_for_action ())); |
99 | 1204 | } | 1220 | } |
100 | 1205 | 1221 | ||
101 | 1222 | |||
102 | 1206 | public static void after_pasting_files (GLib.HashTable? uris, void* pointer) { | 1223 | public static void after_pasting_files (GLib.HashTable? uris, void* pointer) { |
103 | 1207 | if (pointer == null) | 1224 | if (pointer == null) |
104 | 1208 | return; | 1225 | return; |
105 | @@ -1225,7 +1242,7 @@ | |||
106 | 1225 | pasted_files_list.prepend (k as File); | 1242 | pasted_files_list.prepend (k as File); |
107 | 1226 | }); | 1243 | }); |
108 | 1227 | 1244 | ||
110 | 1228 | view.select_glib_files (pasted_files_list, pasted_files_list.first ().data); | 1245 | view.select_glib_files_when_thawed (pasted_files_list, pasted_files_list.first ().data); |
111 | 1229 | return false; | 1246 | return false; |
112 | 1230 | }); | 1247 | }); |
113 | 1231 | } | 1248 | } |
114 | @@ -2562,15 +2579,25 @@ | |||
115 | 2562 | activate_selected_items (Marlin.OpenFlag.DEFAULT); | 2579 | activate_selected_items (Marlin.OpenFlag.DEFAULT); |
116 | 2563 | } | 2580 | } |
117 | 2564 | 2581 | ||
118 | 2582 | uint update_selected_timeout_id = 0; | ||
119 | 2565 | protected virtual void on_view_selection_changed () { | 2583 | protected virtual void on_view_selection_changed () { |
120 | 2584 | /* updating selecting file list is expensive for large selections so throttle */ | ||
121 | 2585 | if (update_selected_timeout_id == 0) { | ||
122 | 2586 | after_selected_files_changed (); /* Make sure first update happens immediately */ | ||
123 | 2587 | update_selected_timeout_id = Timeout.add_full (GLib.Priority.LOW, 100, () => { | ||
124 | 2588 | update_selected_timeout_id = 0; | ||
125 | 2589 | after_selected_files_changed (); | ||
126 | 2590 | return false; | ||
127 | 2591 | }); | ||
128 | 2592 | } | ||
129 | 2593 | } | ||
130 | 2594 | |||
131 | 2595 | private void after_selected_files_changed () { | ||
132 | 2566 | update_selected_files (); | 2596 | update_selected_files (); |
133 | 2567 | update_menu_actions (); | 2597 | update_menu_actions (); |
134 | 2568 | if (is_frozen) | ||
135 | 2569 | return; | ||
136 | 2570 | |||
137 | 2571 | selection_changed (get_selected_files ()); | 2598 | selection_changed (get_selected_files ()); |
138 | 2572 | } | 2599 | } |
140 | 2573 | 2600 | ||
141 | 2574 | /** Keyboard event handling **/ | 2601 | /** Keyboard event handling **/ |
142 | 2575 | 2602 | ||
143 | 2576 | /** Returns true if the code parameter matches the keycode of the keyval parameter for | 2603 | /** Returns true if the code parameter matches the keycode of the keyval parameter for |
144 | @@ -2793,7 +2820,7 @@ | |||
145 | 2793 | linear_select_path (path); | 2820 | linear_select_path (path); |
146 | 2794 | } else if (no_mods) { | 2821 | } else if (no_mods) { |
147 | 2795 | unselect_path (old_path); | 2822 | unselect_path (old_path); |
149 | 2796 | select_path (path); | 2823 | select_path (path, true); /* Cursor follows */ |
150 | 2797 | } | 2824 | } |
151 | 2798 | } | 2825 | } |
152 | 2799 | return true; | 2826 | return true; |
153 | @@ -3153,12 +3180,13 @@ | |||
154 | 3153 | } | 3180 | } |
155 | 3154 | 3181 | ||
156 | 3155 | if (!path_selected && click_zone != ClickZone.HELPER) { | 3182 | if (!path_selected && click_zone != ClickZone.HELPER) { |
158 | 3156 | if (no_mods) | 3183 | if (no_mods) { |
159 | 3157 | unselect_all (); | 3184 | unselect_all (); |
161 | 3158 | 3185 | } | |
162 | 3159 | /* If modifier pressed then default handler determines selection */ | 3186 | /* If modifier pressed then default handler determines selection */ |
165 | 3160 | if (no_mods && !on_blank) | 3187 | if (no_mods && !on_blank) { |
166 | 3161 | select_path (path); | 3188 | select_path (path, true); /* Cursor follows */ |
167 | 3189 | } | ||
168 | 3162 | } | 3190 | } |
169 | 3163 | 3191 | ||
170 | 3164 | bool result = true; | 3192 | bool result = true; |
171 | @@ -3216,7 +3244,7 @@ | |||
172 | 3216 | unselect_path (path); | 3244 | unselect_path (path); |
173 | 3217 | } else { | 3245 | } else { |
174 | 3218 | should_deselect = false; | 3246 | should_deselect = false; |
176 | 3219 | select_path (path); | 3247 | select_path (path); /* Cursor follows */ |
177 | 3220 | } | 3248 | } |
178 | 3221 | } | 3249 | } |
179 | 3222 | 3250 | ||
180 | @@ -3245,7 +3273,7 @@ | |||
181 | 3245 | 3273 | ||
182 | 3246 | case Gdk.BUTTON_SECONDARY: | 3274 | case Gdk.BUTTON_SECONDARY: |
183 | 3247 | if (click_zone == ClickZone.NAME || | 3275 | if (click_zone == ClickZone.NAME || |
185 | 3248 | (click_zone == ClickZone.BLANK_PATH)) { | 3276 | click_zone == ClickZone.BLANK_PATH) { |
186 | 3249 | 3277 | ||
187 | 3250 | select_path (path); | 3278 | select_path (path); |
188 | 3251 | } else if (click_zone == ClickZone.INVALID) { | 3279 | } else if (click_zone == ClickZone.INVALID) { |
189 | @@ -3458,6 +3486,7 @@ | |||
190 | 3458 | cancel_drag_timer (); | 3486 | cancel_drag_timer (); |
191 | 3459 | cancel_timeout (ref drag_scroll_timer_id); | 3487 | cancel_timeout (ref drag_scroll_timer_id); |
192 | 3460 | cancel_timeout (ref add_remove_file_timeout_id); | 3488 | cancel_timeout (ref add_remove_file_timeout_id); |
193 | 3489 | cancel_timeout (ref update_selected_timeout_id); | ||
194 | 3461 | /* List View will take care of unloading subdirectories */ | 3490 | /* List View will take care of unloading subdirectories */ |
195 | 3462 | } | 3491 | } |
196 | 3463 | 3492 | ||
197 | @@ -3498,7 +3527,7 @@ | |||
198 | 3498 | public abstract Gtk.TreePath? get_path_at_cursor (); | 3527 | public abstract Gtk.TreePath? get_path_at_cursor (); |
199 | 3499 | public abstract void select_all (); | 3528 | public abstract void select_all (); |
200 | 3500 | public abstract void unselect_all (); | 3529 | public abstract void unselect_all (); |
202 | 3501 | public abstract void select_path (Gtk.TreePath? path); | 3530 | public abstract void select_path (Gtk.TreePath? path, bool cursor_follows = false); |
203 | 3502 | public abstract void unselect_path (Gtk.TreePath? path); | 3531 | public abstract void unselect_path (Gtk.TreePath? path); |
204 | 3503 | public abstract bool path_is_selected (Gtk.TreePath? path); | 3532 | public abstract bool path_is_selected (Gtk.TreePath? path); |
205 | 3504 | public abstract bool get_visible_range (out Gtk.TreePath? start_path, out Gtk.TreePath? end_path); | 3533 | public abstract bool get_visible_range (out Gtk.TreePath? start_path, out Gtk.TreePath? end_path); |
206 | @@ -3524,6 +3553,8 @@ | |||
207 | 3524 | protected abstract void thaw_tree (); | 3553 | protected abstract void thaw_tree (); |
208 | 3525 | protected new abstract void freeze_child_notify (); | 3554 | protected new abstract void freeze_child_notify (); |
209 | 3526 | protected new abstract void thaw_child_notify (); | 3555 | protected new abstract void thaw_child_notify (); |
210 | 3556 | protected abstract void connect_tree_signals (); | ||
211 | 3557 | protected abstract void disconnect_tree_signals (); | ||
212 | 3527 | protected abstract bool is_on_icon (int x, int y, int orig_x, int orig_y, ref bool on_helper); | 3558 | protected abstract bool is_on_icon (int x, int y, int orig_x, int orig_y, ref bool on_helper); |
213 | 3528 | 3559 | ||
214 | 3529 | /** Unimplemented methods | 3560 | /** Unimplemented methods |
215 | 3530 | 3561 | ||
216 | === modified file 'src/View/AbstractTreeView.vala' | |||
217 | --- src/View/AbstractTreeView.vala 2017-03-01 17:43:40 +0000 | |||
218 | +++ src/View/AbstractTreeView.vala 2017-06-06 09:01:03 +0000 | |||
219 | @@ -64,6 +64,10 @@ | |||
220 | 64 | 64 | ||
221 | 65 | protected void set_up_view () { | 65 | protected void set_up_view () { |
222 | 66 | connect_tree_signals (); | 66 | connect_tree_signals (); |
223 | 67 | tree.realize.connect ((w) => { | ||
224 | 68 | tree.grab_focus (); | ||
225 | 69 | tree.columns_autosize (); | ||
226 | 70 | }); | ||
227 | 67 | } | 71 | } |
228 | 68 | 72 | ||
229 | 69 | protected override void set_up_name_renderer () { | 73 | protected override void set_up_name_renderer () { |
230 | @@ -76,13 +80,11 @@ | |||
231 | 76 | name_renderer.yalign = 0.5f; | 80 | name_renderer.yalign = 0.5f; |
232 | 77 | } | 81 | } |
233 | 78 | 82 | ||
235 | 79 | protected void connect_tree_signals () { | 83 | protected override void connect_tree_signals () { |
236 | 80 | tree.get_selection ().changed.connect (on_view_selection_changed); | 84 | tree.get_selection ().changed.connect (on_view_selection_changed); |
242 | 81 | 85 | } | |
243 | 82 | tree.realize.connect ((w) => { | 86 | protected override void disconnect_tree_signals () { |
244 | 83 | tree.grab_focus (); | 87 | tree.get_selection ().changed.disconnect (on_view_selection_changed); |
240 | 84 | tree.columns_autosize (); | ||
241 | 85 | }); | ||
245 | 86 | } | 88 | } |
246 | 87 | 89 | ||
247 | 88 | protected override Gtk.Widget? create_view () { | 90 | protected override Gtk.Widget? create_view () { |
248 | @@ -131,21 +133,27 @@ | |||
249 | 131 | tree.get_selection ().unselect_all (); | 133 | tree.get_selection ().unselect_all (); |
250 | 132 | } | 134 | } |
251 | 133 | 135 | ||
253 | 134 | public override void select_path (Gtk.TreePath? path) { | 136 | /* Avoid using this function with "cursor_follows = true" to select large numbers of files one by one |
254 | 137 | * It would take an exponentially long time. Use "select_files" function in parent class. | ||
255 | 138 | */ | ||
256 | 139 | public override void select_path (Gtk.TreePath? path, bool cursor_follows = false) { | ||
257 | 135 | if (path != null) { | 140 | if (path != null) { |
258 | 136 | var selection = tree.get_selection (); | 141 | var selection = tree.get_selection (); |
259 | 137 | /* Unlike for IconView, set_cursor unselects previously selected paths (Gtk bug?), | ||
260 | 138 | * so we have to remember them and reselect afterwards */ | ||
261 | 139 | GLib.List<Gtk.TreePath> selected_paths = null; | ||
262 | 140 | selection.selected_foreach ((m, p, i) => { | ||
263 | 141 | selected_paths.prepend (p); | ||
264 | 142 | }); | ||
265 | 143 | /* Ensure cursor follows last selection */ | ||
266 | 144 | tree.set_cursor (path, null, false); /* This selects path but unselects rest! */ | ||
267 | 145 | selection.select_path (path); | 142 | selection.select_path (path); |
271 | 146 | selected_paths.@foreach ((p) => { | 143 | if (cursor_follows) { |
272 | 147 | selection.select_path (p); | 144 | /* Unlike for IconView, set_cursor unselects previously selected paths (Gtk bug?), |
273 | 148 | }); | 145 | * so we have to remember them and reselect afterwards */ |
274 | 146 | GLib.List<Gtk.TreePath> selected_paths = null; | ||
275 | 147 | selection.selected_foreach ((m, p, i) => { | ||
276 | 148 | selected_paths.prepend (p); | ||
277 | 149 | }); | ||
278 | 150 | /* Ensure cursor follows last selection */ | ||
279 | 151 | tree.set_cursor (path, null, false); /* This selects path but unselects rest! */ | ||
280 | 152 | |||
281 | 153 | selected_paths.@foreach ((p) => { | ||
282 | 154 | selection.select_path (p); | ||
283 | 155 | }); | ||
284 | 156 | } | ||
285 | 149 | } | 157 | } |
286 | 150 | } | 158 | } |
287 | 151 | public override void unselect_path (Gtk.TreePath? path) { | 159 | public override void unselect_path (Gtk.TreePath? path) { |
288 | 152 | 160 | ||
289 | === modified file 'src/View/IconView.vala' | |||
290 | --- src/View/IconView.vala 2017-03-01 17:43:40 +0000 | |||
291 | +++ src/View/IconView.vala 2017-06-06 09:01:03 +0000 | |||
292 | @@ -49,6 +49,9 @@ | |||
293 | 49 | (tree as Gtk.CellLayout).add_attribute (icon_renderer, "file", FM.ListModel.ColumnID.FILE_COLUMN); | 49 | (tree as Gtk.CellLayout).add_attribute (icon_renderer, "file", FM.ListModel.ColumnID.FILE_COLUMN); |
294 | 50 | 50 | ||
295 | 51 | connect_tree_signals (); | 51 | connect_tree_signals (); |
296 | 52 | tree.realize.connect ((w) => { | ||
297 | 53 | tree.grab_focus (); | ||
298 | 54 | }); | ||
299 | 52 | } | 55 | } |
300 | 53 | 56 | ||
301 | 54 | protected override void set_up_name_renderer () { | 57 | protected override void set_up_name_renderer () { |
302 | @@ -63,12 +66,11 @@ | |||
303 | 63 | } | 66 | } |
304 | 64 | 67 | ||
305 | 65 | 68 | ||
307 | 66 | private void connect_tree_signals () { | 69 | protected override void connect_tree_signals () { |
308 | 67 | tree.selection_changed.connect (on_view_selection_changed); | 70 | tree.selection_changed.connect (on_view_selection_changed); |
313 | 68 | 71 | } | |
314 | 69 | tree.realize.connect ((w) => { | 72 | protected override void disconnect_tree_signals () { |
315 | 70 | tree.grab_focus (); | 73 | tree.selection_changed.disconnect (on_view_selection_changed); |
312 | 71 | }); | ||
316 | 72 | } | 74 | } |
317 | 73 | 75 | ||
318 | 74 | protected override Gtk.Widget? create_view () { | 76 | protected override Gtk.Widget? create_view () { |
319 | @@ -134,14 +136,19 @@ | |||
320 | 134 | } | 136 | } |
321 | 135 | 137 | ||
322 | 136 | public override void unselect_all () { | 138 | public override void unselect_all () { |
324 | 137 | tree.unselect_all (); | 139 | tree.unselect_all (); |
325 | 138 | } | 140 | } |
326 | 139 | 141 | ||
328 | 140 | public override void select_path (Gtk.TreePath? path) { | 142 | /* Avoid using this function with "cursor_follows = true" to select large numbers of files one by one |
329 | 143 | * It would take an exponentially long time. Use "select_files" function in parent class. | ||
330 | 144 | */ | ||
331 | 145 | public override void select_path (Gtk.TreePath? path, bool cursor_follows = false) { | ||
332 | 141 | if (path != null) { | 146 | if (path != null) { |
336 | 142 | /* Ensure cursor follows last selection */ | 147 | tree.select_path (path); /* This selects path but does not unselect the rest (unlike TreeView) */ |
337 | 143 | tree.set_cursor (path, null, false); /* This selects path but does not unselect the rest (unlike TreeView) */ | 148 | |
338 | 144 | tree.select_path (path); | 149 | if (cursor_follows) { |
339 | 150 | tree.set_cursor (path, null, false); | ||
340 | 151 | } | ||
341 | 145 | } | 152 | } |
342 | 146 | } | 153 | } |
343 | 147 | 154 | ||
344 | @@ -362,22 +369,23 @@ | |||
345 | 362 | end_path = direction_change ? last_selected : first_selected; | 369 | end_path = direction_change ? last_selected : first_selected; |
346 | 363 | } | 370 | } |
347 | 364 | 371 | ||
348 | 372 | /* Cursor follows when selecting path */ | ||
349 | 365 | if (before_first) { | 373 | if (before_first) { |
350 | 366 | do { | 374 | do { |
351 | 367 | p2 = p.copy (); | 375 | p2 = p.copy (); |
353 | 368 | select_path (p); | 376 | select_path (p, true); |
354 | 369 | p.next (); | 377 | p.next (); |
355 | 370 | } while (p.compare (p2) != 0 && p.compare (end_path) <= 0); | 378 | } while (p.compare (p2) != 0 && p.compare (end_path) <= 0); |
356 | 371 | } else if (after_last) { | 379 | } else if (after_last) { |
357 | 372 | do { | 380 | do { |
359 | 373 | select_path (p); | 381 | select_path (p, true); |
360 | 374 | p2 = p.copy (); | 382 | p2 = p.copy (); |
361 | 375 | p.prev (); | 383 | p.prev (); |
362 | 376 | } while (p.compare (p2) != 0 && p.compare (end_path) >= 0); | 384 | } while (p.compare (p2) != 0 && p.compare (end_path) >= 0); |
363 | 377 | } else {/* between first and last */ | 385 | } else {/* between first and last */ |
364 | 378 | do { | 386 | do { |
365 | 379 | p2 = p.copy (); | 387 | p2 = p.copy (); |
367 | 380 | select_path (p); | 388 | select_path (p, true); |
368 | 381 | p.prev (); | 389 | p.prev (); |
369 | 382 | } while (p.compare (p2) != 0 && p.compare (first_selected) >= 0); | 390 | } while (p.compare (p2) != 0 && p.compare (first_selected) >= 0); |
370 | 383 | 391 | ||
371 | 384 | 392 | ||
372 | === modified file 'src/View/Slot.vala' | |||
373 | --- src/View/Slot.vala 2017-02-04 10:57:49 +0000 | |||
374 | +++ src/View/Slot.vala 2017-06-06 09:01:03 +0000 | |||
375 | @@ -350,7 +350,7 @@ | |||
376 | 350 | 350 | ||
377 | 351 | public override void select_glib_files (GLib.List<GLib.File> files, GLib.File? focus_location) { | 351 | public override void select_glib_files (GLib.List<GLib.File> files, GLib.File? focus_location) { |
378 | 352 | if (dir_view != null) | 352 | if (dir_view != null) |
380 | 353 | dir_view.select_glib_files (files, focus_location); | 353 | dir_view.select_glib_files_when_thawed (files, focus_location); |
381 | 354 | } | 354 | } |
382 | 355 | 355 | ||
383 | 356 | public void select_gof_file (GOF.File gof) { | 356 | public void select_gof_file (GOF.File gof) { |
I agree that this branch seems to alleviate the issue, but it does not make it disappear entirely.