OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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.display = "none"; | 58 _selectionDiv.style.setProperty("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.display = "none"; | 98 _selectionDiv.style.setProperty("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 BoundingBox getBoundingBoxForRange(CellRange r) { | 105 Future<BoundingBox> getBoundingBoxForRange(CellRange r) { |
106 assert(window.inMeasurementFrame); | |
107 // Modify the overlay for entire row/column selection | 106 // Modify the overlay for entire row/column selection |
108 int minRow = r.minCorner.row; | 107 int minRow = r.minCorner.row; |
109 int maxRow = r.maxCorner.row; | 108 int maxRow = r.maxCorner.row; |
110 int minCol = r.minCorner.col; | 109 int minCol = r.minCorner.col; |
111 int maxCol = r.maxCorner.col; | 110 int maxCol = r.maxCorner.col; |
112 | 111 |
113 // FIXME - provide the table size to this class in a better way | 112 // FIXME - provide the table size to this class in a better way |
114 int maxVisibleRow = _table.numRows() - 1; | 113 int maxVisibleRow = _table.numRows() - 1; |
115 TableRowElement tableRow = _table.getRowElement(0); | 114 TableRowElement tableRow = _table.getRowElement(0); |
116 int maxVisibleCol = tableRow.cells.length - 1; | 115 int maxVisibleCol = tableRow.cells.length - 1; |
(...skipping 28 matching lines...) Expand all Loading... |
145 TableRowElement minRowElmt = _table.getRowElement(minPinned); | 144 TableRowElement minRowElmt = _table.getRowElement(minPinned); |
146 TableCellElement minCellElmt = | 145 TableCellElement minCellElmt = |
147 minRowElmt.cells[_pin(minCol - _originColumn, 1, maxVisibleCol)]; | 146 minRowElmt.cells[_pin(minCol - _originColumn, 1, maxVisibleCol)]; |
148 int maxPinned = _pin(maxRow - _originRow, 1, maxVisibleRow); | 147 int maxPinned = _pin(maxRow - _originRow, 1, maxVisibleRow); |
149 TableRowElement maxRowElmt = _table.getRowElement(maxPinned); | 148 TableRowElement maxRowElmt = _table.getRowElement(maxPinned); |
150 TableCellElement maxCellElmt = | 149 TableCellElement maxCellElmt = |
151 maxRowElmt.cells[_pin(maxCol - _originColumn, 1, maxVisibleCol)]; | 150 maxRowElmt.cells[_pin(maxCol - _originColumn, 1, maxVisibleCol)]; |
152 | 151 |
153 // We need bounding box relative to the container which will be offset by | 152 // We need bounding box relative to the container which will be offset by |
154 // css. | 153 // css. |
| 154 final tableRect = _table.rect; |
| 155 final minCellElmtRect = minCellElmt.rect; |
| 156 final maxCellElmtRect = maxCellElmt.rect; |
155 | 157 |
156 final orgP = _table.rect.bounding; | 158 window.requestLayoutFrame(() { |
157 final minP = minCellElmt.rect.bounding; | 159 ClientRect orgP = tableRect.value.bounding; |
158 final maxP = maxCellElmt.rect.bounding; | 160 ClientRect minP = minCellElmtRect.value.bounding; |
159 return new BoundingBox( | 161 ClientRect maxP = maxCellElmtRect.value.bounding; |
160 (minP.left - orgP.left).toInt(), | 162 completer.complete(new BoundingBox( |
161 (minP.top - orgP.top).toInt(), | 163 (minP.left - orgP.left).toInt(), |
162 (maxP.left - minP.left + maxCellElmt.rect.client.width).toInt(), | 164 (minP.top - orgP.top).toInt(), |
163 (maxP.top - minP.top + maxCellElmt.rect.client.height).toInt()); | 165 (maxP.left - minP.left + maxCellElmtRect.value.client.width).toInt(), |
| 166 (maxP.top - minP.top + maxCellElmtRect.value.client.height).toInt())); |
| 167 }); |
| 168 return completer.future; |
164 } | 169 } |
165 | 170 |
166 CellRange getSelectionRange() => _getSelectionRange(_selectedCell, _selectionC
orner); | 171 CellRange getSelectionRange() => _getSelectionRange(_selectedCell, _selectionC
orner); |
167 | 172 |
168 bool isColumnSelected(int column) { | 173 bool isColumnSelected(int column) { |
169 CellRange r = _getSelectionRange(_selectedCell, _selectionCorner); | 174 CellRange r = _getSelectionRange(_selectedCell, _selectionCorner); |
170 if (r == null) { | 175 if (r == null) { |
171 return false; | 176 return false; |
172 } | 177 } |
173 return r.minCorner.col <= column && r.maxCorner.col >= column; | 178 return r.minCorner.col <= column && r.maxCorner.col >= column; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 } | 220 } |
216 | 221 |
217 // Draw or hide the selection marquee using current cell metrics | 222 // Draw or hide the selection marquee using current cell metrics |
218 void updateSelection() { | 223 void updateSelection() { |
219 CellRange r = _getSelectionRange(_selectedCell, _selectionCorner); | 224 CellRange r = _getSelectionRange(_selectedCell, _selectionCorner); |
220 if (r == null) { | 225 if (r == null) { |
221 clearSelection(); | 226 clearSelection(); |
222 return; | 227 return; |
223 } | 228 } |
224 | 229 |
225 window.requestMeasurementFrame(() { | 230 getBoundingBoxForRange(r).then((BoundingBox box) { |
226 final box = getBoundingBoxForRange(r); | 231 if (box != null) { |
227 return () { | 232 _selectionDiv.style.setProperty("left", HtmlUtils.toPx(box.left)); |
228 if (box != null) { | 233 _selectionDiv.style.setProperty("top", HtmlUtils.toPx(box.top)); |
229 _selectionDiv.style.left = HtmlUtils.toPx(box.left); | 234 _selectionDiv.style.setProperty("width", HtmlUtils.toPx(box.width)); |
230 _selectionDiv.style.top = HtmlUtils.toPx(box.top); | 235 _selectionDiv.style.setProperty("height", HtmlUtils.toPx(box.height)); |
231 _selectionDiv.style.width = HtmlUtils.toPx(box.width); | 236 _selectionDiv.style.removeProperty("display"); |
232 _selectionDiv.style.height = HtmlUtils.toPx(box.height); | 237 } else { |
233 _selectionDiv.style.removeProperty("display"); | 238 _selectionDiv.style.setProperty("display", "none"); |
234 } else { | 239 } |
235 _selectionDiv.style.display = "none"; | |
236 } | |
237 }; | |
238 }); | 240 }); |
239 } | 241 } |
240 | 242 |
241 // Return the selection range for the given corners. | 243 // Return the selection range for the given corners. |
242 // If the first corner is on a row header, it is assumed that the entire | 244 // If the first corner is on a row header, it is assumed that the entire |
243 // row is selected, and the returned range has min.col == 0 and max.col == 0. | 245 // row is selected, and the returned range has min.col == 0 and max.col == 0. |
244 // If the first corner is on a column header, it is assumed that the entire | 246 // If the first corner is on a column header, it is assumed that the entire |
245 // column is selected, and the returned range has min.row == 0 and max.row ==
0. | 247 // column is selected, and the returned range has min.row == 0 and max.row ==
0. |
246 CellRange _getSelectionRange(CellLocation corner1, CellLocation corner2) { | 248 CellRange _getSelectionRange(CellLocation corner1, CellLocation corner2) { |
247 if (corner1 == null || corner2 == null) { | 249 if (corner1 == null || corner2 == null) { |
(...skipping 19 matching lines...) Expand all Loading... |
267 return new CellRange.columns(corner1.spreadsheet, minCol, maxCol); | 269 return new CellRange.columns(corner1.spreadsheet, minCol, maxCol); |
268 } | 270 } |
269 | 271 |
270 return new CellRange(corner1.spreadsheet, new RowCol(minRow, minCol), | 272 return new CellRange(corner1.spreadsheet, new RowCol(minRow, minCol), |
271 new RowCol(maxRow, maxCol)); | 273 new RowCol(maxRow, maxCol)); |
272 } | 274 } |
273 | 275 |
274 // Return the value closest to x in the range [min, max] | 276 // Return the value closest to x in the range [min, max] |
275 int _pin(int x, int min, int max) => Math.max(Math.min(x, max), min); | 277 int _pin(int x, int min, int max) => Math.max(Math.min(x, max), min); |
276 } | 278 } |
OLD | NEW |