OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 'use strict'; | |
6 | |
7 /** | |
8 * Drag selector used on the file list or the grid table. | |
9 * TODO(hirono): Support drag selection for grid view. crbug.com/224832 | |
10 * @constructor | |
11 */ | |
12 function DragSelector() { | |
13 /** | |
14 * Target list of drag selection. | |
15 * @type {cr.ui.List} | |
16 * @private | |
17 */ | |
18 this.target_ = null; | |
19 | |
20 /** | |
21 * Border element of drag handle. | |
22 * @type {HtmlElement} | |
23 * @private | |
24 */ | |
25 this.border_ = null; | |
26 | |
27 /** | |
28 * Start point of dragging. | |
29 * @type {number?} | |
30 * @private | |
31 */ | |
32 this.startX_ = null; | |
33 | |
34 /** | |
35 * Start point of dragging. | |
36 * @type {number?} | |
37 * @private | |
38 */ | |
39 this.startY_ = null; | |
40 | |
41 /** | |
42 * Indexes of selected items by dragging at the last update. | |
43 * @type {Array.<number>} | |
44 * @private | |
45 */ | |
46 this.lastSelection_ = null; | |
yoshiki
2013/06/04 01:16:01
nit: I think it is better to assign '[]' instead o
hirono
2013/06/04 02:16:10
Done.
| |
47 | |
48 /** | |
49 * Indexes of selected items at the start of dragging. | |
50 * @type {Array.<number>} | |
51 * @private | |
52 */ | |
53 this.originalSelection_ = null; | |
yoshiki
2013/06/04 01:16:01
ditto
hirono
2013/06/04 02:16:10
Done.
| |
54 | |
55 // Bind handlers to make them removable. | |
56 this.onMouseMoveBound_ = this.onMouseMove_.bind(this); | |
57 this.onMouseUpBound_ = this.onMouseUp_.bind(this); | |
58 } | |
59 | |
60 /** | |
61 * Flag that shows whether the item is included in the selection or not. | |
62 * @enum {number} | |
63 * @private | |
64 */ | |
65 DragSelector.SelectionFlag_ = { | |
66 IN_LAST_SELECTION: 1 << 0, | |
67 IN_CURRENT_SELECTION: 1 << 1 | |
68 }; | |
69 | |
70 /** | |
71 * Starts drag selection by reacting dragstart event. | |
72 * This function must be called from handlers of dragstart event. | |
73 * | |
74 * @this {DragSelector} | |
75 * @param {cr.ui.List} list List where the drag selection starts. | |
76 * @param {Event} event The dragstart event. | |
77 */ | |
78 DragSelector.prototype.startDragSelection = function(list, event) { | |
79 // Precondition check | |
80 if (!list.selectionModel_.multiple || this.target_) | |
81 return; | |
82 | |
83 // Set the target of the drag selection | |
84 this.target_ = list; | |
85 | |
86 // Create and add the border element | |
87 if (!this.border_) { | |
88 this.border_ = this.target_.ownerDocument.createElement('div'); | |
89 this.border_.className = 'drag-selection-border'; | |
90 } | |
91 list.appendChild(this.border_); | |
92 | |
93 // Prevent default action. | |
94 event.preventDefault(); | |
95 | |
96 // If no modifier key is pressed, clear the original selection. | |
97 if (!event.shiftKey && !event.ctrlKey) { | |
98 this.target_.selectionModel_.unselectAll(); | |
99 } | |
100 | |
101 // Save the start state. | |
102 var rect = list.getBoundingClientRect(); | |
103 this.startX_ = event.clientX - rect.left + list.scrollLeft; | |
104 this.startY_ = event.clientY - rect.top + list.scrollTop; | |
105 this.border_.style.left = this.startX_ + 'px'; | |
106 this.border_.style.top = this.startY_ + 'px'; | |
107 this.lastSelection_ = []; | |
108 this.originalSelection_ = this.target_.selectionModel_.selectedIndexes; | |
109 | |
110 // Register event handlers. | |
111 // The handlers are bounded at the constructor. | |
112 this.target_.ownerDocument.addEventListener( | |
113 'mousemove', this.onMouseMoveBound_, true); | |
114 this.target_.ownerDocument.addEventListener( | |
115 'mouseup', this.onMouseUpBound_, true); | |
116 }; | |
117 | |
118 /** | |
119 * Handle the mousemove event. | |
120 * @private | |
121 * @param {MouseEvent} event The mousemove event. | |
122 */ | |
123 DragSelector.prototype.onMouseMove_ = function(event) { | |
124 // Get the selection bounds. | |
125 var inRect = this.target_.getBoundingClientRect(); | |
126 var x = event.clientX - inRect.left + this.target_.scrollLeft; | |
127 var y = event.clientY - inRect.top + this.target_.scrollTop; | |
128 var borderBounds = { | |
129 left: Math.max(Math.min(this.startX_, x), 0), | |
130 top: Math.max(Math.min(this.startY_, y), 0), | |
131 right: Math.min(Math.max(this.startX_, x), this.target_.scrollWidth), | |
132 bottom: Math.min(Math.max(this.startY_, y), this.target_.scrollHeight) | |
133 }; | |
134 borderBounds.width = borderBounds.right - borderBounds.left; | |
135 borderBounds.height = borderBounds.bottom - borderBounds.top; | |
136 this.border_.style.left = borderBounds.left + 'px'; | |
137 this.border_.style.top = borderBounds.top + 'px'; | |
138 this.border_.style.width = borderBounds.width + 'px'; | |
139 this.border_.style.height = borderBounds.height + 'px'; | |
140 | |
141 // Collect items within the selection rect. | |
142 var currentSelection = []; | |
143 var leadIndex = -1; | |
144 for (var i = 0; i < this.target_.selectionModel_.length; i++) { | |
145 var itemMetrics = this.target_.getHeightsForIndex_(i); | |
yoshiki
2013/06/04 01:16:01
getHeightsForIndex_() is private. Please make it p
yoshiki
2013/06/04 01:50:42
Sorry, I was wrong. I did grep 'getHeightForIndex_
hirono
2013/06/04 02:16:10
I added a TODO comment.
| |
146 itemMetrics.bottom = itemMetrics.top + itemMetrics.height; | |
147 if (itemMetrics.top < borderBounds.bottom && | |
148 itemMetrics.bottom >= borderBounds.top) { | |
149 currentSelection.push(i); | |
150 } | |
151 var pointed = itemMetrics.top <= y && y < itemMetrics.bottom; | |
152 if (pointed) | |
153 leadIndex = i; | |
154 } | |
155 | |
156 // Diff the selection between currentSelection and this.lastSelection_. | |
157 var selectionFlag = []; | |
158 for (var i = 0; i < this.lastSelection_.length; i++) { | |
159 var index = this.lastSelection_[i]; | |
160 // Bit operator can be used for undefined value. | |
161 selectionFlag[index] = | |
162 selectionFlag[index] | DragSelector.SelectionFlag_.IN_LAST_SELECTION; | |
163 } | |
164 for (var i = 0; i < currentSelection.length; i++) { | |
165 var index = currentSelection[i]; | |
166 // Bit operator can be used for undefined value. | |
167 selectionFlag[index] = | |
168 selectionFlag[index] | DragSelector.SelectionFlag_.IN_CURRENT_SELECTION; | |
169 } | |
170 | |
171 // Update the selection | |
172 this.target_.selectionModel_.beginChange(); | |
173 for (var name in selectionFlag) { | |
174 var index = parseInt(name); | |
175 var flag = selectionFlag[name]; | |
176 // flag may be one of followings: | |
mtomasz
2013/06/04 01:01:21
nit: flag -> Flag / The flag
hirono
2013/06/04 01:08:08
Done.
| |
177 // - IN_LAST_SELECTION | IN_CURRENT_SELECTION | |
178 // - IN_LAST_SELECTION | |
179 // - IN_CURRENT_SELECTION | |
180 // - undefined | |
181 | |
182 // If flag equals to (IN_LAST_SELECTION | IN_CURRENT_SELECTION), | |
mtomasz
2013/06/04 01:01:21
nit: flag -> the flag
hirono
2013/06/04 01:08:08
Done.
| |
183 // this is included in both the last selection and the current selection. | |
mtomasz
2013/06/04 01:01:21
nit: this -> then this item / then the item
hirono
2013/06/04 01:08:08
Done.
| |
184 // We have nothing to do for this item. | |
185 | |
186 if (flag == DragSelector.SelectionFlag_.IN_LAST_SELECTION) { | |
187 // If flag equals to IN_LAST_SELECTION, | |
mtomasz
2013/06/04 01:01:21
nit: flag -> the flag
hirono
2013/06/04 01:08:08
Done.
| |
188 // this is included in lastSelection but not in currentSelection. | |
189 // Revert the selection state to this.originalSelection_. | |
190 this.target_.selectionModel_.setIndexSelected( | |
191 index, this.originalSelection_.indexOf(index) != -1); | |
192 } else if (flag == DragSelector.SelectionFlag_.IN_CURRENT_SELECTION) { | |
193 // If flag equals to IN_CURRENT_SELECTION, | |
194 // this is included in currentSelection but no in lastSelection. | |
mtomasz
2013/06/04 01:01:21
nit: no in -> not in
hirono
2013/06/04 01:08:08
Done.
| |
195 this.target_.selectionModel_.setIndexSelected(index, true); | |
196 } | |
197 } | |
198 if (leadIndex != -1) { | |
199 this.target_.selectionModel_.leadIndex = leadIndex; | |
200 this.target_.selectionModel_.anchorIndex = leadIndex; | |
201 } | |
202 this.target_.selectionModel_.endChange(); | |
203 this.lastSelection_ = currentSelection; | |
204 }; | |
205 | |
206 /** | |
207 * Handle the mouseup event. | |
208 * @private | |
209 * @param {MouseEvent} event The mouseup event. | |
210 */ | |
211 DragSelector.prototype.onMouseUp_ = function(event) { | |
212 this.onMouseMove_(event); | |
213 this.target_.removeChild(this.border_); | |
214 this.target_.ownerDocument.removeEventListener( | |
215 'mousemove', this.onMouseMoveBound_, true); | |
216 this.target_.ownerDocument.removeEventListener( | |
217 'mouseup', this.onMouseUpBound_, true); | |
218 event.stopPropagation(); | |
219 this.target_ = null; | |
220 }; | |
OLD | NEW |