| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // require: array_data_model.js | 5 // require: array_data_model.js |
| 6 // require: list_selection_model.js | 6 // require: list_selection_model.js |
| 7 // require: list_selection_controller.js | 7 // require: list_selection_controller.js |
| 8 // require: list_item.js | 8 // require: list_item.js |
| 9 | 9 |
| 10 /** | 10 /** |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 126 this.boundHandleDataModelPermuted_); | 126 this.boundHandleDataModelPermuted_); |
| 127 this.dataModel_.removeEventListener('change', | 127 this.dataModel_.removeEventListener('change', |
| 128 this.boundHandleDataModelChange_); | 128 this.boundHandleDataModelChange_); |
| 129 this.dataModel_.removeEventListener('splice', | 129 this.dataModel_.removeEventListener('splice', |
| 130 this.boundHandleDataModelChange_); | 130 this.boundHandleDataModelChange_); |
| 131 } | 131 } |
| 132 | 132 |
| 133 this.dataModel_ = dataModel; | 133 this.dataModel_ = dataModel; |
| 134 | 134 |
| 135 this.cachedItems_ = {}; | 135 this.cachedItems_ = {}; |
| 136 this.cachedItemSizes_ = {}; | 136 this.cachedItemHeights_ = {}; |
| 137 this.selectionModel.clear(); | 137 this.selectionModel.clear(); |
| 138 if (dataModel) | 138 if (dataModel) |
| 139 this.selectionModel.adjustLength(dataModel.length); | 139 this.selectionModel.adjustLength(dataModel.length); |
| 140 | 140 |
| 141 if (this.dataModel_) { | 141 if (this.dataModel_) { |
| 142 this.dataModel_.addEventListener( | 142 this.dataModel_.addEventListener( |
| 143 'permuted', | 143 'permuted', |
| 144 this.boundHandleDataModelPermuted_); | 144 this.boundHandleDataModelPermuted_); |
| 145 this.dataModel_.addEventListener('change', | 145 this.dataModel_.addEventListener('change', |
| 146 this.boundHandleDataModelChange_); | 146 this.boundHandleDataModelChange_); |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 324 this.addEventListener('blur', this.handleElementBlur_, true); | 324 this.addEventListener('blur', this.handleElementBlur_, true); |
| 325 this.addEventListener('scroll', this.handleScroll.bind(this)); | 325 this.addEventListener('scroll', this.handleScroll.bind(this)); |
| 326 this.setAttribute('role', 'listbox'); | 326 this.setAttribute('role', 'listbox'); |
| 327 | 327 |
| 328 // Make list focusable | 328 // Make list focusable |
| 329 if (!this.hasAttribute('tabindex')) | 329 if (!this.hasAttribute('tabindex')) |
| 330 this.tabIndex = 0; | 330 this.tabIndex = 0; |
| 331 }, | 331 }, |
| 332 | 332 |
| 333 /** | 333 /** |
| 334 * @param {ListItem=} item The list item to measure. |
| 335 * @return {number} The height of the given item. If the fixed height on CSS |
| 336 * is set by 'px', uses that value as height. Otherwise, measures the size. |
| 337 * @private |
| 338 */ |
| 339 measureItemHeight_: function(item) { |
| 340 var height = item.style.height; |
| 341 // Use the fixed height if set it on CSS, to save a time of layout |
| 342 // calculation. |
| 343 if (height && height.substr(-2) == 'px') |
| 344 return parseInt(height.substr(0, height.length - 2)); |
| 345 |
| 346 return this.measureItem(item).height; |
| 347 }, |
| 348 |
| 349 /** |
| 334 * @return {number} The height of default item, measuring it if necessary. | 350 * @return {number} The height of default item, measuring it if necessary. |
| 335 * @private | 351 * @private |
| 336 */ | 352 */ |
| 337 getDefaultItemHeight_: function() { | 353 getDefaultItemHeight_: function() { |
| 338 return this.getDefaultItemSize_().height; | 354 return this.getDefaultItemSize_().height; |
| 339 }, | 355 }, |
| 340 | 356 |
| 341 /** | 357 /** |
| 342 * @param {number} index The index of the item. | 358 * @param {number} index The index of the item. |
| 343 * @return {number} The height of the item. | 359 * @return {number} The height of the item, measuring it if necessary. |
| 344 */ | 360 */ |
| 345 getItemHeightByIndex_: function(index) { | 361 getItemHeightByIndex_: function(index) { |
| 346 // If |this.fixedHeight_| is true, all the rows have same default height. | 362 // If |this.fixedHeight_| is true, all the rows have same default height. |
| 347 if (this.fixedHeight_) | 363 if (this.fixedHeight_) |
| 348 return this.getDefaultItemHeight_(); | 364 return this.getDefaultItemHeight_(); |
| 349 | 365 |
| 350 if (this.cachedItemSizes_[index]) | 366 if (this.cachedItemHeights_[index]) |
| 351 return this.cachedItemSizes_[index].height; | 367 return this.cachedItemHeights_[index]; |
| 352 | 368 |
| 353 var item = this.getListItemByIndex(index); | 369 var item = this.getListItemByIndex(index); |
| 354 if (item) | 370 if (item) |
| 355 return this.getItemSize_(item).height; | 371 return this.measureItemHeight_(item); |
| 356 | 372 |
| 357 return this.getDefaultItemHeight_(); | 373 return this.getDefaultItemHeight_(); |
| 358 }, | 374 }, |
| 359 | 375 |
| 360 /** | 376 /** |
| 361 * @return {number} The width of default item, measuring it if necessary. | |
| 362 * @private | |
| 363 */ | |
| 364 getDefaultItemWidth_: function() { | |
| 365 return this.getDefaultItemSize_().width; | |
| 366 }, | |
| 367 | |
| 368 /** | |
| 369 * @return {{height: number, width: number}} The height and width | 377 * @return {{height: number, width: number}} The height and width |
| 370 * of default item, measuring it if necessary. | 378 * of default item, measuring it if necessary. |
| 371 * @private | 379 * @private |
| 372 */ | 380 */ |
| 373 getDefaultItemSize_: function() { | 381 getDefaultItemSize_: function() { |
| 374 if (!this.measured_ || !this.measured_.height) { | 382 if (!this.measured_ || !this.measured_.height) { |
| 375 this.measured_ = this.measureItem(); | 383 this.measured_ = this.measureItem(); |
| 376 } | 384 } |
| 377 return this.measured_; | 385 return this.measured_; |
| 378 }, | 386 }, |
| 379 | 387 |
| 380 /** | 388 /** |
| 381 * @return {{height: number, width: number}} The height and width | |
| 382 * of an item, measuring it if necessary. | |
| 383 * @private | |
| 384 */ | |
| 385 getItemSize_: function(item) { | |
| 386 if (this.cachedItemSizes_[item.listIndex]) | |
| 387 return this.cachedItemSizes_[item.listIndex]; | |
| 388 | |
| 389 var size = this.measureItem(item); | |
| 390 if (!isNaN(size.height) && !isNaN(size.weight)) | |
| 391 this.cachedItemSizes_[item.listIndex] = size; | |
| 392 | |
| 393 return size; | |
| 394 }, | |
| 395 | |
| 396 /** | |
| 397 * Creates an item (dataModel.item(0)) and measures its height. The item is | 389 * Creates an item (dataModel.item(0)) and measures its height. The item is |
| 398 * cached instead of creating a new one every time.. | 390 * cached instead of creating a new one every time.. |
| 399 * @param {ListItem=} opt_item The list item to use to do the measuring. If | 391 * @param {ListItem=} opt_item The list item to use to do the measuring. If |
| 400 * this is not provided an item will be created based on the first value | 392 * this is not provided an item will be created based on the first value |
| 401 * in the model. | 393 * in the model. |
| 402 * @return {{height: number, marginTop: number, marginBottom:number, | 394 * @return {{height: number, marginTop: number, marginBottom:number, |
| 403 * width: number, marginLeft: number, marginRight:number}} | 395 * width: number, marginLeft: number, marginRight:number}} |
| 404 * The height and width of the item, taking | 396 * The height and width of the item, taking |
| 405 * margins into account, and the top, bottom, left and right margins | 397 * margins into account, and the top, bottom, left and right margins |
| 406 * themselves. | 398 * themselves. |
| (...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 628 var newCachedItems = {}; | 620 var newCachedItems = {}; |
| 629 for (var index in this.cachedItems_) { | 621 for (var index in this.cachedItems_) { |
| 630 if (e.permutation[index] != -1) { | 622 if (e.permutation[index] != -1) { |
| 631 var newIndex = e.permutation[index]; | 623 var newIndex = e.permutation[index]; |
| 632 newCachedItems[newIndex] = this.cachedItems_[index]; | 624 newCachedItems[newIndex] = this.cachedItems_[index]; |
| 633 newCachedItems[newIndex].listIndex = newIndex; | 625 newCachedItems[newIndex].listIndex = newIndex; |
| 634 } | 626 } |
| 635 } | 627 } |
| 636 this.cachedItems_ = newCachedItems; | 628 this.cachedItems_ = newCachedItems; |
| 637 | 629 |
| 638 var newCachedItemSizes = {}; | 630 var newCachedItemHeights = {}; |
| 639 for (var index in this.cachedItemSizes_) { | 631 for (var index in this.cachedItemHeights_) { |
| 640 if (e.permutation[index] != -1) { | 632 if (e.permutation[index] != -1) { |
| 641 newCachedItemSizes[e.permutation[index]] = | 633 newCachedItemHeights[e.permutation[index]] = |
| 642 this.cachedItemSizes_[index]; | 634 this.cachedItemHeights_[index]; |
| 643 } | 635 } |
| 644 } | 636 } |
| 645 this.cachedItemSizes_ = newCachedItemSizes; | 637 this.cachedItemHeights_ = newCachedItemHeights; |
| 646 | 638 |
| 647 this.startBatchUpdates(); | 639 this.startBatchUpdates(); |
| 648 | 640 |
| 649 var sm = this.selectionModel; | 641 var sm = this.selectionModel; |
| 650 sm.adjustLength(e.newLength); | 642 sm.adjustLength(e.newLength); |
| 651 sm.adjustToReordering(e.permutation); | 643 sm.adjustToReordering(e.permutation); |
| 652 | 644 |
| 653 this.endBatchUpdates(); | 645 this.endBatchUpdates(); |
| 654 }, | 646 }, |
| 655 | 647 |
| 656 handleDataModelChange_: function(e) { | 648 handleDataModelChange_: function(e) { |
| 657 delete this.cachedItems_[e.index]; | 649 delete this.cachedItems_[e.index]; |
| 658 delete this.cachedItemSizes_[e.index]; | 650 delete this.cachedItemHeights_[e.index]; |
| 659 this.cachedMeasuredItem_ = null; | 651 this.cachedMeasuredItem_ = null; |
| 660 | 652 |
| 661 if (e.index >= this.firstIndex_ && | 653 if (e.index >= this.firstIndex_ && |
| 662 (e.index < this.lastIndex_ || this.remainingSpace_)) { | 654 (e.index < this.lastIndex_ || this.remainingSpace_)) { |
| 663 this.redraw(); | 655 this.redraw(); |
| 664 } | 656 } |
| 665 }, | 657 }, |
| 666 | 658 |
| 667 /** | 659 /** |
| 668 * @param {number} index The index of the item. | 660 * @param {number} index The index of the item. |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 984 while (currentIndex < lastIndex) | 976 while (currentIndex < lastIndex) |
| 985 insert(this); | 977 insert(this); |
| 986 }, | 978 }, |
| 987 | 979 |
| 988 /** | 980 /** |
| 989 * Ensures that all the item sizes in the list have been already cached. | 981 * Ensures that all the item sizes in the list have been already cached. |
| 990 */ | 982 */ |
| 991 ensureAllItemSizesInCache: function() { | 983 ensureAllItemSizesInCache: function() { |
| 992 var measuringIndexes = []; | 984 var measuringIndexes = []; |
| 993 for (var y = 0; y < this.dataModel.length; y++) { | 985 for (var y = 0; y < this.dataModel.length; y++) { |
| 994 if (!this.cachedItemSizes_[y]) | 986 if (!this.cachedItemHeights_[y]) |
| 995 measuringIndexes.push(y); | 987 measuringIndexes.push(y); |
| 996 } | 988 } |
| 997 | 989 |
| 998 var measuringItems = []; | 990 var measuringItems = []; |
| 999 // Adds temporary elements. | 991 // Adds temporary elements. |
| 1000 for (var y = 0; y < measuringIndexes.length; y++) { | 992 for (var y = 0; y < measuringIndexes.length; y++) { |
| 1001 var index = measuringIndexes[y]; | 993 var index = measuringIndexes[y]; |
| 1002 var dataItem = this.dataModel.item(index); | 994 var dataItem = this.dataModel.item(index); |
| 1003 var listItem = this.cachedItems_[index] || this.createItem(dataItem); | 995 var listItem = this.cachedItems_[index] || this.createItem(dataItem); |
| 1004 listItem.listIndex = index; | 996 listItem.listIndex = index; |
| 1005 this.appendChild(listItem); | 997 this.appendChild(listItem); |
| 1006 this.cachedItems_[index] = listItem; | 998 this.cachedItems_[index] = listItem; |
| 1007 measuringItems.push(listItem); | 999 measuringItems.push(listItem); |
| 1008 } | 1000 } |
| 1009 | 1001 |
| 1010 // All mesurings must be placed after adding all the elements, to prevent | 1002 // All mesurings must be placed after adding all the elements, to prevent |
| 1011 // performance reducing. | 1003 // performance reducing. |
| 1012 for (var y = 0; y < measuringIndexes.length; y++) { | 1004 for (var y = 0; y < measuringIndexes.length; y++) { |
| 1013 var index = measuringIndexes[y]; | 1005 var index = measuringIndexes[y]; |
| 1014 this.cachedItemSizes_[index] = this.measureItem(measuringItems[y]); | 1006 this.cachedItemHeights_[index] = |
| 1007 this.measureItemHeight_(measuringItems[y]); |
| 1015 } | 1008 } |
| 1016 | 1009 |
| 1017 // Removes all the temprary elements. | 1010 // Removes all the temprary elements. |
| 1018 for (var y = 0; y < measuringIndexes.length; y++) { | 1011 for (var y = 0; y < measuringIndexes.length; y++) { |
| 1019 this.removeChild(measuringItems[y]); | 1012 this.removeChild(measuringItems[y]); |
| 1020 } | 1013 } |
| 1021 }, | 1014 }, |
| 1022 | 1015 |
| 1023 /** | 1016 /** |
| 1024 * Returns the height of after filler in the list. | 1017 * Returns the height of after filler in the list. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1062 | 1055 |
| 1063 // We cache the list items since creating the DOM nodes is the most | 1056 // We cache the list items since creating the DOM nodes is the most |
| 1064 // expensive part of redrawing. | 1057 // expensive part of redrawing. |
| 1065 var cachedItems = this.cachedItems_ || {}; | 1058 var cachedItems = this.cachedItems_ || {}; |
| 1066 var newCachedItems = {}; | 1059 var newCachedItems = {}; |
| 1067 | 1060 |
| 1068 var autoExpands = this.autoExpands_; | 1061 var autoExpands = this.autoExpands_; |
| 1069 var scrollTop = this.scrollTop; | 1062 var scrollTop = this.scrollTop; |
| 1070 var clientHeight = this.clientHeight; | 1063 var clientHeight = this.clientHeight; |
| 1071 | 1064 |
| 1072 var lastItemHeights = this.getHeightsForIndex_(dataModel.length - 1); | |
| 1073 var desiredScrollHeight = lastItemHeights.top + lastItemHeights.height; | |
| 1074 | |
| 1075 var itemsInViewPort = this.getItemsInViewPort(scrollTop, clientHeight); | 1065 var itemsInViewPort = this.getItemsInViewPort(scrollTop, clientHeight); |
| 1076 // Draws the hidden rows just above/below the viewport to prevent | 1066 // Draws the hidden rows just above/below the viewport to prevent |
| 1077 // flashing in scroll. | 1067 // flashing in scroll. |
| 1078 var firstIndex = Math.max(0, itemsInViewPort.first - 1); | 1068 var firstIndex = Math.max(0, itemsInViewPort.first - 1); |
| 1079 var lastIndex = Math.min(itemsInViewPort.last + 1, dataModel.length); | 1069 var lastIndex = Math.min(itemsInViewPort.last + 1, dataModel.length); |
| 1080 | 1070 |
| 1081 var beforeFillerHeight = | 1071 var beforeFillerHeight = |
| 1082 this.autoExpands ? 0 : this.getItemTop(firstIndex); | 1072 this.autoExpands ? 0 : this.getItemTop(firstIndex); |
| 1083 var afterFillerHeight = | 1073 var afterFillerHeight = |
| 1084 this.autoExpands ? 0 : this.getAfterFillerHeight(lastIndex); | 1074 this.autoExpands ? 0 : this.getAfterFillerHeight(lastIndex); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1120 this.firstIndex_ = firstIndex; | 1110 this.firstIndex_ = firstIndex; |
| 1121 this.lastIndex_ = lastIndex; | 1111 this.lastIndex_ = lastIndex; |
| 1122 | 1112 |
| 1123 this.remainingSpace_ = itemsInViewPort.last > dataModel.length; | 1113 this.remainingSpace_ = itemsInViewPort.last > dataModel.length; |
| 1124 this.cachedItems_ = newCachedItems; | 1114 this.cachedItems_ = newCachedItems; |
| 1125 | 1115 |
| 1126 // Mesurings must be placed after adding all the elements, to prevent | 1116 // Mesurings must be placed after adding all the elements, to prevent |
| 1127 // performance reducing. | 1117 // performance reducing. |
| 1128 if (!this.fixedHeight_) { | 1118 if (!this.fixedHeight_) { |
| 1129 for (var y = firstIndex; y < lastIndex; y++) | 1119 for (var y = firstIndex; y < lastIndex; y++) |
| 1130 this.cachedItemSizes_[y] = this.measureItem(newCachedItems[y]); | 1120 this.cachedItemHeights_[y] = |
| 1121 this.measureItemHeight_(newCachedItems[y]); |
| 1131 } | 1122 } |
| 1132 | 1123 |
| 1133 // Measure again in case the item height has changed due to a page zoom. | 1124 // Measure again in case the item height has changed due to a page zoom. |
| 1134 // | 1125 // |
| 1135 // The measure above is only done the first time but this measure is done | 1126 // The measure above is only done the first time but this measure is done |
| 1136 // after every redraw. It is done in a timeout so it will not trigger | 1127 // after every redraw. It is done in a timeout so it will not trigger |
| 1137 // a reflow (which made the redraw speed 3 times slower on my system). | 1128 // a reflow (which made the redraw speed 3 times slower on my system). |
| 1138 // By using a timeout the measuring will happen later when there is no | 1129 // By using a timeout the measuring will happen later when there is no |
| 1139 // need for a reflow. | 1130 // need for a reflow. |
| 1140 if (listItem && this.fixedHeight_) { | 1131 if (listItem && this.fixedHeight_) { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1183 * because list items can contain controls that can be focused, and for some | 1174 * because list items can contain controls that can be focused, and for some |
| 1184 * purposes (e.g., styling), the list can still be conceptually focused at | 1175 * purposes (e.g., styling), the list can still be conceptually focused at |
| 1185 * that point even though it doesn't actually have the page focus. | 1176 * that point even though it doesn't actually have the page focus. |
| 1186 */ | 1177 */ |
| 1187 cr.defineProperty(List, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR); | 1178 cr.defineProperty(List, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR); |
| 1188 | 1179 |
| 1189 return { | 1180 return { |
| 1190 List: List | 1181 List: List |
| 1191 } | 1182 } |
| 1192 }); | 1183 }); |
| OLD | NEW |