OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // The callback method type invoked when the selection changes. | 5 // The callback method type invoked when the selection changes. |
6 typedef void SelectionCallback(); | 6 typedef void SelectionCallback(); |
7 | 7 |
8 // The method type for the SelectionManager.applyToSelectedCells method. | 8 // The method type for the SelectionManager.applyToSelectedCells method. |
9 typedef void SelectionApply(CellLocation location); | 9 typedef void SelectionApply(CellLocation location); |
10 | 10 |
11 class BoundingBox { | 11 class BoundingBox { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 Spreadsheet get spreadsheet() => _spreadsheet; | 48 Spreadsheet get spreadsheet() => _spreadsheet; |
49 | 49 |
50 SelectionManager(this._spreadsheet, SpreadsheetPresenter presenter, Window win
dow, this._table) | 50 SelectionManager(this._spreadsheet, SpreadsheetPresenter presenter, Window win
dow, this._table) |
51 : _selectedCell = null, _selectionCorner = null, _originRow = 0, _originCo
lumn = 0 { | 51 : _selectedCell = null, _selectionCorner = null, _originRow = 0, _originCo
lumn = 0 { |
52 Document doc = window.document; | 52 Document doc = window.document; |
53 | 53 |
54 Element spreadsheetElement = presenter.spreadsheetElement; | 54 Element spreadsheetElement = presenter.spreadsheetElement; |
55 _selectionDiv = new Element.tag("div"); | 55 _selectionDiv = new Element.tag("div"); |
56 _selectionDiv.id = "selection-${_spreadsheet.name}"; | 56 _selectionDiv.id = "selection-${_spreadsheet.name}"; |
57 _selectionDiv.attributes["class"] = "selection"; | 57 _selectionDiv.attributes["class"] = "selection"; |
58 _selectionDiv.style.setProperty("display", "none"); | 58 _selectionDiv.style.display = "none"; |
59 spreadsheetElement.nodes.add(_selectionDiv); | 59 spreadsheetElement.nodes.add(_selectionDiv); |
60 | 60 |
61 Element thumb = new Element.tag("div"); | 61 Element thumb = new Element.tag("div"); |
62 thumb.id = "selection-thumb-${_spreadsheet.name}"; | 62 thumb.id = "selection-thumb-${_spreadsheet.name}"; |
63 thumb.attributes["class"] = "selection-thumb"; | 63 thumb.attributes["class"] = "selection-thumb"; |
64 _selectionDiv.nodes.add(thumb); | 64 _selectionDiv.nodes.add(thumb); |
65 | 65 |
66 // Update selection after changing zoom factor, to reduce problems with posi
tion precision | 66 // Update selection after changing zoom factor, to reduce problems with posi
tion precision |
67 // Only create a single tracker per page | 67 // Only create a single tracker per page |
68 if (_zoomTracker == null) { | 68 if (_zoomTracker == null) { |
(...skipping 19 matching lines...) Expand all Loading... |
88 CellRange range = _getSelectionRange(_selectedCell, _selectionCorner); | 88 CellRange range = _getSelectionRange(_selectedCell, _selectionCorner); |
89 if (range == null) { | 89 if (range == null) { |
90 return; | 90 return; |
91 } | 91 } |
92 | 92 |
93 range.forEach(apply); | 93 range.forEach(apply); |
94 } | 94 } |
95 | 95 |
96 // Hide the selection marquee and empty the selection range | 96 // Hide the selection marquee and empty the selection range |
97 void clearSelection() { | 97 void clearSelection() { |
98 _selectionDiv.style.setProperty("display", "none"); | 98 _selectionDiv.style.display = "none"; |
99 _selectedCell = _selectionCorner = null; | 99 _selectedCell = _selectionCorner = null; |
100 selectionChanged(); | 100 selectionChanged(); |
101 } | 101 } |
102 | 102 |
103 // Return a BoundingBox for the given CellRange, clipped to the visible region
of the table | 103 // Return a BoundingBox for the given CellRange, clipped to the visible region
of the table |
104 // TODO: deal with full row and/or column selection | 104 // TODO: deal with full row and/or column selection |
105 Future<BoundingBox> getBoundingBoxForRange(CellRange r) { | 105 BoundingBox getBoundingBoxForRange(CellRange r) { |
| 106 assert(window.inMeasurementFrame); |
106 // Modify the overlay for entire row/column selection | 107 // Modify the overlay for entire row/column selection |
107 int minRow = r.minCorner.row; | 108 int minRow = r.minCorner.row; |
108 int maxRow = r.maxCorner.row; | 109 int maxRow = r.maxCorner.row; |
109 int minCol = r.minCorner.col; | 110 int minCol = r.minCorner.col; |
110 int maxCol = r.maxCorner.col; | 111 int maxCol = r.maxCorner.col; |
111 | 112 |
112 // FIXME - provide the table size to this class in a better way | 113 // FIXME - provide the table size to this class in a better way |
113 int maxVisibleRow = _table.numRows() - 1; | 114 int maxVisibleRow = _table.numRows() - 1; |
114 TableRowElement tableRow = _table.getRowElement(0); | 115 TableRowElement tableRow = _table.getRowElement(0); |
115 int maxVisibleCol = tableRow.cells.length - 1; | 116 int maxVisibleCol = tableRow.cells.length - 1; |
(...skipping 28 matching lines...) Expand all Loading... |
144 TableRowElement minRowElmt = _table.getRowElement(minPinned); | 145 TableRowElement minRowElmt = _table.getRowElement(minPinned); |
145 TableCellElement minCellElmt = | 146 TableCellElement minCellElmt = |
146 minRowElmt.cells[_pin(minCol - _originColumn, 1, maxVisibleCol)]; | 147 minRowElmt.cells[_pin(minCol - _originColumn, 1, maxVisibleCol)]; |
147 int maxPinned = _pin(maxRow - _originRow, 1, maxVisibleRow); | 148 int maxPinned = _pin(maxRow - _originRow, 1, maxVisibleRow); |
148 TableRowElement maxRowElmt = _table.getRowElement(maxPinned); | 149 TableRowElement maxRowElmt = _table.getRowElement(maxPinned); |
149 TableCellElement maxCellElmt = | 150 TableCellElement maxCellElmt = |
150 maxRowElmt.cells[_pin(maxCol - _originColumn, 1, maxVisibleCol)]; | 151 maxRowElmt.cells[_pin(maxCol - _originColumn, 1, maxVisibleCol)]; |
151 | 152 |
152 // We need bounding box relative to the container which will be offset by | 153 // We need bounding box relative to the container which will be offset by |
153 // css. | 154 // css. |
154 final tableRect = _table.rect; | |
155 final minCellElmtRect = minCellElmt.rect; | |
156 final maxCellElmtRect = maxCellElmt.rect; | |
157 | 155 |
158 window.requestLayoutFrame(() { | 156 final orgP = _table.rect.bounding; |
159 ClientRect orgP = tableRect.value.bounding; | 157 final minP = minCellElmt.rect.bounding; |
160 ClientRect minP = minCellElmtRect.value.bounding; | 158 final maxP = maxCellElmt.rect.bounding; |
161 ClientRect maxP = maxCellElmtRect.value.bounding; | 159 return new BoundingBox( |
162 completer.complete(new BoundingBox( | 160 (minP.left - orgP.left).toInt(), |
163 (minP.left - orgP.left).toInt(), | 161 (minP.top - orgP.top).toInt(), |
164 (minP.top - orgP.top).toInt(), | 162 (maxP.left - minP.left + maxCellElmt.rect.client.width).toInt(), |
165 (maxP.left - minP.left + maxCellElmtRect.value.client.width).toInt(), | 163 (maxP.top - minP.top + maxCellElmt.rect.client.height).toInt()); |
166 (maxP.top - minP.top + maxCellElmtRect.value.client.height).toInt())); | |
167 }); | |
168 return completer.future; | |
169 } | 164 } |
170 | 165 |
171 CellRange getSelectionRange() => _getSelectionRange(_selectedCell, _selectionC
orner); | 166 CellRange getSelectionRange() => _getSelectionRange(_selectedCell, _selectionC
orner); |
172 | 167 |
173 bool isColumnSelected(int column) { | 168 bool isColumnSelected(int column) { |
174 CellRange r = _getSelectionRange(_selectedCell, _selectionCorner); | 169 CellRange r = _getSelectionRange(_selectedCell, _selectionCorner); |
175 if (r == null) { | 170 if (r == null) { |
176 return false; | 171 return false; |
177 } | 172 } |
178 return r.minCorner.col <= column && r.maxCorner.col >= column; | 173 return r.minCorner.col <= column && r.maxCorner.col >= column; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
220 } | 215 } |
221 | 216 |
222 // Draw or hide the selection marquee using current cell metrics | 217 // Draw or hide the selection marquee using current cell metrics |
223 void updateSelection() { | 218 void updateSelection() { |
224 CellRange r = _getSelectionRange(_selectedCell, _selectionCorner); | 219 CellRange r = _getSelectionRange(_selectedCell, _selectionCorner); |
225 if (r == null) { | 220 if (r == null) { |
226 clearSelection(); | 221 clearSelection(); |
227 return; | 222 return; |
228 } | 223 } |
229 | 224 |
230 getBoundingBoxForRange(r).then((BoundingBox box) { | 225 window.requestMeasurementFrame(() { |
231 if (box != null) { | 226 final box = getBoundingBoxForRange(r); |
232 _selectionDiv.style.setProperty("left", HtmlUtils.toPx(box.left)); | 227 return () { |
233 _selectionDiv.style.setProperty("top", HtmlUtils.toPx(box.top)); | 228 if (box != null) { |
234 _selectionDiv.style.setProperty("width", HtmlUtils.toPx(box.width)); | 229 _selectionDiv.style.left = HtmlUtils.toPx(box.left); |
235 _selectionDiv.style.setProperty("height", HtmlUtils.toPx(box.height)); | 230 _selectionDiv.style.top = HtmlUtils.toPx(box.top); |
236 _selectionDiv.style.removeProperty("display"); | 231 _selectionDiv.style.width = HtmlUtils.toPx(box.width); |
237 } else { | 232 _selectionDiv.style.height = HtmlUtils.toPx(box.height); |
238 _selectionDiv.style.setProperty("display", "none"); | 233 _selectionDiv.style.removeProperty("display"); |
239 } | 234 } else { |
| 235 _selectionDiv.style.display = "none"; |
| 236 } |
| 237 }; |
240 }); | 238 }); |
241 } | 239 } |
242 | 240 |
243 // Return the selection range for the given corners. | 241 // Return the selection range for the given corners. |
244 // If the first corner is on a row header, it is assumed that the entire | 242 // If the first corner is on a row header, it is assumed that the entire |
245 // row is selected, and the returned range has min.col == 0 and max.col == 0. | 243 // row is selected, and the returned range has min.col == 0 and max.col == 0. |
246 // If the first corner is on a column header, it is assumed that the entire | 244 // If the first corner is on a column header, it is assumed that the entire |
247 // column is selected, and the returned range has min.row == 0 and max.row ==
0. | 245 // column is selected, and the returned range has min.row == 0 and max.row ==
0. |
248 CellRange _getSelectionRange(CellLocation corner1, CellLocation corner2) { | 246 CellRange _getSelectionRange(CellLocation corner1, CellLocation corner2) { |
249 if (corner1 == null || corner2 == null) { | 247 if (corner1 == null || corner2 == null) { |
(...skipping 19 matching lines...) Expand all Loading... |
269 return new CellRange.columns(corner1.spreadsheet, minCol, maxCol); | 267 return new CellRange.columns(corner1.spreadsheet, minCol, maxCol); |
270 } | 268 } |
271 | 269 |
272 return new CellRange(corner1.spreadsheet, new RowCol(minRow, minCol), | 270 return new CellRange(corner1.spreadsheet, new RowCol(minRow, minCol), |
273 new RowCol(maxRow, maxCol)); | 271 new RowCol(maxRow, maxCol)); |
274 } | 272 } |
275 | 273 |
276 // Return the value closest to x in the range [min, max] | 274 // Return the value closest to x in the range [min, max] |
277 int _pin(int x, int min, int max) => Math.max(Math.min(x, max), min); | 275 int _pin(int x, int min, int max) => Math.max(Math.min(x, max), min); |
278 } | 276 } |
OLD | NEW |