Changeset 241652 in webkit
- Timestamp:
- Feb 17, 2019 12:49:38 PM (5 years ago)
- Location:
- trunk
- Files:
-
- 3 deleted
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r241647 r241652 1 2019-02-17 Matt Baker <mattbaker@apple.com> 2 3 Web Inspector: Frontend performance is very slow reloading theverge.com - 50% of time in TreeOutline _indexOfTreeElement 4 https://bugs.webkit.org/show_bug.cgi?id=193605 5 <rdar://problem/47403986> 6 7 Reviewed by Devin Rousso. 8 9 * inspector/table/resources/table-utilities.js: 10 (TestPage.registerInitializer.InspectorTest.TableDataSource.prototype.tableIndexForRepresentedObject): 11 (TestPage.registerInitializer.InspectorTest.TableDataSource.prototype.tableRepresentedObjectForIndex): 12 (TestPage.registerInitializer.InspectorTest.TableDataSource): 13 New Table data source methods. 14 15 * inspector/unit-tests/index-set-expected.txt: Removed. 16 * inspector/unit-tests/index-set.html: Removed. 17 * inspector/unit-tests/set-utilities-expected.txt: 18 * inspector/unit-tests/set-utilities.html: 19 Remove IndexSet tests and update tests for Set utilities to include new 20 helper methods `equals` and `difference`, and `firstValue`. 21 1 22 2019-02-16 Zalan Bujtas <zalan@apple.com> 2 23 -
trunk/LayoutTests/inspector/table/resources/table-utilities.js
r237495 r241652 12 12 { 13 13 return this._items.length; 14 } 15 16 tableIndexForRepresentedObject(table, object) 17 { 18 return this._items.indexOf(object); 19 } 20 21 tableRepresentedObjectForIndex(table, index) 22 { 23 return this._items[index]; 14 24 } 15 25 }; -
trunk/LayoutTests/inspector/unit-tests/set-utilities-expected.txt
r237436 r241652 17 17 PASS: a set should not be a subset of another set with same and different values. 18 18 19 -- Running test case: Set.prototype.equals 20 PASS: an empty set should be equal to another empty set. 21 PASS: a set should be equal to another set with the same values. 22 PASS: a set should be equal to another set with the same values in a different order. 23 PASS: a set should not be a equal to another set with different values. 24 PASS: a set should not be equal to another set with same and different values. 25 26 -- Running test case: Set.prototype.difference 27 Given a Set with values [], and another Set with values []: 28 PASS: Set difference should be []. 29 30 Given a Set with values [1,2,3], and another Set with values []: 31 PASS: Set difference should be [1,2,3]. 32 33 Given a Set with values [], and another Set with values [1,2,3]: 34 PASS: Set difference should be []. 35 36 Given a Set with values [1,2,3], and another Set with values [2,3,4]: 37 PASS: Set difference should be [1]. 38 39 40 -- Running test case: Set.prototype.firstValue 41 PASS: Set with values [] should have firstValue equal to undefined. 42 PASS: Set with values [1,2,3] should have firstValue equal to 1. 43 -
trunk/LayoutTests/inspector/unit-tests/set-utilities.html
r237436 r241652 60 60 }); 61 61 62 suite.addTestCase({ 63 name: "Set.prototype.equals", 64 test() { 65 function testTrue(a, b, message) { 66 InspectorTest.expectThat((new Set(a)).equals(new Set(b)), message); 67 } 68 69 function testFalse(a, b, message) { 70 InspectorTest.expectFalse((new Set(a)).equals(new Set(b)), message); 71 } 72 73 const object1 = {a: 1}; 74 const object2 = {b: 2}; 75 const object3 = {c: 3}; 76 77 testTrue([], [], "an empty set should be equal to another empty set."); 78 testTrue([1, "a", object1], [1, "a", object1], "a set should be equal to another set with the same values."); 79 testTrue([1, "a", object1], [object1, 1, "a"], "a set should be equal to another set with the same values in a different order."); 80 testFalse([1, "a", object1], [2, "b", object2], "a set should not be a equal to another set with different values."); 81 testFalse([1, 2, "a", "b", object1, object2], [1, 3, "a", "c", object1, object3], "a set should not be equal to another set with same and different values."); 82 83 return true; 84 } 85 }); 86 87 suite.addTestCase({ 88 name: "Set.prototype.difference", 89 test() { 90 function testDifference({aValues, bValues, expectedDifference}) { 91 let a = new Set(aValues); 92 let b = new Set(bValues); 93 94 InspectorTest.log(`Given a Set with values [${aValues}], and another Set with values [${bValues}]:`); 95 96 let difference = a.difference(b); 97 InspectorTest.expectThat(difference.equals(new Set(expectedDifference)), `Set difference should be [${expectedDifference}].`); 98 InspectorTest.log(""); 99 } 100 101 testDifference({ 102 aValues: [], 103 bValues: [], 104 expectedDifference: [], 105 }); 106 107 testDifference({ 108 aValues: [1, 2, 3], 109 bValues: [], 110 expectedDifference: [1, 2, 3], 111 }); 112 113 testDifference({ 114 aValues: [], 115 bValues: [1, 2, 3], 116 expectedDifference: [], 117 }); 118 119 testDifference({ 120 aValues: [1, 2, 3], 121 bValues: [2, 3, 4], 122 expectedDifference: [1], 123 }); 124 125 return true; 126 } 127 }); 128 129 suite.addTestCase({ 130 name: "Set.prototype.firstValue", 131 test() { 132 function testFirstValue(values) { 133 InspectorTest.expectEqual(new Set(values).firstValue, values[0], `Set with values [${values}] should have firstValue equal to ${values[0]}.`); 134 } 135 136 testFirstValue([]); 137 testFirstValue([1, 2, 3]); 138 } 139 }); 140 62 141 suite.runTestCasesAndFinish(); 63 142 } -
trunk/Source/WebInspectorUI/ChangeLog
r241643 r241652 1 2019-02-17 Matt Baker <mattbaker@apple.com> 2 3 Web Inspector: Frontend performance is very slow reloading theverge.com - 50% of time in TreeOutline _indexOfTreeElement 4 https://bugs.webkit.org/show_bug.cgi?id=193605 5 <rdar://problem/47403986> 6 7 Reviewed by Devin Rousso. 8 9 SelectionController should track an unordered Set of represented objects 10 instead of an ordered set of indexes. This eliminates the costly and 11 error-prone updates needed to keep the selected indexes in sync as items 12 are added and removed from TreeOutline (and Table, to a far lesser extent). 13 14 The SelectionController interface is largely the same. Class and delegate 15 methods have been renamed to reflect the change from indexes to objects. 16 SelectionController tracks selected items in selection order. For the 17 operations that rely on objects being in insertion order, the controller 18 uses a comparator function provided at construction time. 19 20 * UserInterface/Base/IndexSet.js: Removed. 21 No longer used. SelectionController now uses a plain Set. 22 23 * UserInterface/Base/Utilities.js: 24 (value): 25 (get return): 26 Add utilities previously supplied by IndexSet and used by SelectionController. 27 28 * UserInterface/Controllers/SelectionController.js: 29 (WI.SelectionController): 30 (WI.SelectionController.prototype.get lastSelectedItem): 31 (WI.SelectionController.prototype.get selectedItems): 32 (WI.SelectionController.prototype.set allowsMultipleSelection): 33 (WI.SelectionController.prototype.hasSelectedItem): 34 (WI.SelectionController.prototype.selectItem): 35 (WI.SelectionController.prototype.deselectItem): 36 (WI.SelectionController.prototype.selectAll): 37 (WI.SelectionController.prototype.deselectAll): 38 (WI.SelectionController.prototype.removeSelectedItems): 39 (WI.SelectionController.prototype.reset): 40 (WI.SelectionController.prototype.didRemoveItems): 41 (WI.SelectionController.prototype.handleKeyDown): 42 (WI.SelectionController.prototype.handleItemMouseDown): 43 (WI.SelectionController.prototype._deselectAllAndSelect): 44 (WI.SelectionController.prototype._selectItemsFromArrowKey): 45 (WI.SelectionController.prototype._firstSelectableItem): 46 (WI.SelectionController.prototype._lastSelectableItem): 47 (WI.SelectionController.prototype._previousSelectableItem): 48 (WI.SelectionController.prototype._nextSelectableItem): 49 (WI.SelectionController.prototype._updateSelectedItems): 50 (WI.SelectionController.prototype._addRange): 51 (WI.SelectionController.prototype._deleteRange): 52 (WI.SelectionController.prototype.get numberOfItems): Deleted. 53 (WI.SelectionController.prototype.didInsertItem): Deleted. 54 (WI.SelectionController.prototype.handleItemMouseDown.normalizeRange): Deleted. 55 (WI.SelectionController.prototype._nextSelectableIndex): Deleted. 56 (WI.SelectionController.prototype._previousSelectableIndex): Deleted. 57 58 * UserInterface/Main.html: 59 * UserInterface/Test.html: 60 Remove IndexSet. 61 62 * UserInterface/Views/CookieStorageContentView.js: 63 (WI.CookieStorageContentView.prototype.tableIndexForRepresentedObject): 64 (WI.CookieStorageContentView.prototype.tableRepresentedObjectForIndex): 65 66 * UserInterface/Views/DOMTreeOutline.js: 67 (WI.DOMTreeOutline.prototype.objectForSelection): 68 69 * UserInterface/Views/NetworkTableContentView.js: 70 (WI.NetworkTableContentView.prototype.tableIndexForRepresentedObject): 71 (WI.NetworkTableContentView.prototype.tableRepresentedObjectForIndex): 72 73 * UserInterface/Views/Table.js: 74 (WI.Table): 75 (WI.Table.prototype.get selectedRow): 76 (WI.Table.prototype.get selectedRows): 77 (WI.Table.prototype.isRowSelected): 78 (WI.Table.prototype.selectRow): 79 (WI.Table.prototype.deselectRow): 80 (WI.Table.prototype.removeRow): 81 (WI.Table.prototype.removeSelectedRows): 82 (WI.Table.prototype.selectionControllerSelectionDidChange): 83 (WI.Table.prototype.selectionControllerFirstSelectableItem): 84 (WI.Table.prototype.selectionControllerLastSelectableItem): 85 (WI.Table.prototype.selectionControllerPreviousSelectableItem): 86 (WI.Table.prototype.selectionControllerNextSelectableItem): 87 (WI.Table.prototype._handleMouseDown): 88 (WI.Table.prototype._removeRows): 89 (WI.Table.prototype._indexForRepresentedObject): 90 (WI.Table.prototype._representedObjectForIndex): 91 (WI.Table.prototype.selectionControllerNumberOfItems): Deleted. 92 (WI.Table.prototype.selectionControllerNextSelectableIndex): Deleted. 93 (WI.Table.prototype.selectionControllerPreviousSelectableIndex): Deleted. 94 (WI.Table.prototype._toggleSelectedRowStyle): Deleted. 95 96 * UserInterface/Views/TreeOutline.js: 97 (WI.TreeOutline.compareSiblings): 98 (WI.TreeOutline): 99 (WI.TreeOutline.prototype.get selectedTreeElement): 100 (WI.TreeOutline.prototype.set selectedTreeElement): 101 (WI.TreeOutline.prototype.get selectedTreeElements): 102 (WI.TreeOutline.prototype.removeChildAtIndex): 103 (WI.TreeOutline.prototype.removeChildren): 104 (WI.TreeOutline.prototype._rememberTreeElement): 105 (WI.TreeOutline.prototype.getCachedTreeElement): 106 (WI.TreeOutline.prototype.selectionControllerSelectionDidChange): 107 (WI.TreeOutline.prototype.selectionControllerFirstSelectableItem): 108 (WI.TreeOutline.prototype.selectionControllerLastSelectableItem): 109 (WI.TreeOutline.prototype.selectionControllerPreviousSelectableItem): 110 (WI.TreeOutline.prototype.selectionControllerNextSelectableItem): 111 (WI.TreeOutline.prototype.objectForSelection): 112 (WI.TreeOutline._generateStyleRulesIfNeeded): 113 (WI.TreeOutline.prototype.selectionControllerNextSelectableIndex): Deleted. 114 (WI.TreeOutline.prototype.selectionControllerPreviousSelectableIndex): Deleted. 115 (WI.TreeOutline._generateStyleRulesIfNeeded._indexesForSubtree.numberOfElementsInSubtree): Deleted. 116 1 117 2019-02-15 Joseph Pecoraro <pecoraro@apple.com> 2 118 -
trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js
r240824 r241652 119 119 }); 120 120 121 Object.defineProperty(Set.prototype, "equals", 122 { 123 value(other) 124 { 125 return this.size === other.size && this.isSubsetOf(other); 126 } 127 }); 128 129 Object.defineProperty(Set.prototype, "difference", 130 { 131 value(other) 132 { 133 if (other === this) 134 return new Set; 135 136 let result = new Set; 137 for (let item of this) { 138 if (!other.has(item)) 139 result.add(item); 140 } 141 142 return result; 143 } 144 }); 145 146 Object.defineProperty(Set.prototype, "firstValue", 147 { 148 get() 149 { 150 return this.values().next().value; 151 } 152 }); 153 121 154 Object.defineProperty(Set.prototype, "intersects", 122 155 { … … 1457 1490 value(value, comparator) 1458 1491 { 1492 function defaultComparator(a, b) 1493 { 1494 return a - b; 1495 } 1496 comparator = comparator || defaultComparator; 1497 1459 1498 var index = this.lowerBound(value, comparator); 1460 1499 return index < this.length && comparator(value, this[index]) === 0 ? index : -1; -
trunk/Source/WebInspectorUI/UserInterface/Controllers/SelectionController.js
r240594 r241652 1 1 /* 2 * Copyright (C) 2018 Apple Inc. All Rights Reserved.2 * Copyright (C) 2018, 2019 Apple Inc. All Rights Reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 26 26 WI.SelectionController = class SelectionController extends WI.Object 27 27 { 28 constructor(delegate )28 constructor(delegate, comparator) 29 29 { 30 30 super(); 31 31 32 32 console.assert(delegate); 33 console.assert(typeof comparator === "function"); 34 33 35 this._delegate = delegate; 36 this._comparator = comparator; 34 37 35 38 this._allowsEmptySelection = true; 36 39 this._allowsMultipleSelection = false; 37 this._lastSelectedI ndex = NaN;38 this._shiftAnchorI ndex = NaN;39 this._selectedI ndexes = new WI.IndexSet;40 this._lastSelectedItem = null; 41 this._shiftAnchorItem = null; 42 this._selectedItems = new Set; 40 43 this._suppressSelectionDidChange = false; 41 44 42 console.assert(this._delegate.selectionControllerNumberOfItems, "SelectionController delegate must implement selectionControllerNumberOfItems."); 43 console.assert(this._delegate.selectionControllerNextSelectableIndex, "SelectionController delegate must implement selectionControllerNextSelectableIndex."); 44 console.assert(this._delegate.selectionControllerPreviousSelectableIndex, "SelectionController delegate must implement selectionControllerPreviousSelectableIndex."); 45 console.assert(this._delegate.selectionControllerFirstSelectableItem, "SelectionController delegate must implement selectionControllerFirstSelectableItem."); 46 console.assert(this._delegate.selectionControllerLastSelectableItem, "SelectionController delegate must implement selectionControllerLastSelectableItem."); 47 console.assert(this._delegate.selectionControllerNextSelectableItem, "SelectionController delegate must implement selectionControllerNextSelectableItem."); 48 console.assert(this._delegate.selectionControllerPreviousSelectableItem, "SelectionController delegate must implement selectionControllerPreviousSelectableItem."); 45 49 } 46 50 … … 48 52 49 53 get delegate() { return this._delegate; } 50 get lastSelectedItem() { return this._lastSelectedI ndex; }51 get selectedItems() { return this._selectedI ndexes; }54 get lastSelectedItem() { return this._lastSelectedItem; } 55 get selectedItems() { return this._selectedItems; } 52 56 53 57 get allowsEmptySelection() { return this._allowsEmptySelection; } … … 68 72 return; 69 73 70 if (this._selectedIndexes.size > 1) { 71 console.assert(this._lastSelectedIndex >= 0); 72 this._updateSelectedItems(new WI.IndexSet([this._lastSelectedIndex])); 73 } 74 } 75 76 get numberOfItems() 77 { 78 return this._delegate.selectionControllerNumberOfItems(this); 79 } 80 81 hasSelectedItem(index) 82 { 83 return this._selectedIndexes.has(index); 84 } 85 86 selectItem(index, extendSelection = false) 87 { 74 if (this._selectedItems.size > 1) 75 this._updateSelectedItems(new Set([this._lastSelectedItem])); 76 } 77 78 hasSelectedItem(item) 79 { 80 return this._selectedItems.has(item); 81 } 82 83 selectItem(item, extendSelection = false) 84 { 85 console.assert(item, "Invalid item for selection."); 88 86 console.assert(!extendSelection || this._allowsMultipleSelection, "Cannot extend selection with multiple selection disabled."); 89 console.assert(index >= 0 && index < this.numberOfItems);90 87 91 88 if (!this._allowsMultipleSelection) 92 89 extendSelection = false; 93 90 94 if (this.hasSelectedItem(i ndex)) {91 if (this.hasSelectedItem(item)) { 95 92 if (!extendSelection) 96 this._deselectAllAndSelect(index); 97 return; 98 } 99 100 let newSelectedItems = extendSelection ? this._selectedIndexes.copy() : new WI.IndexSet; 101 newSelectedItems.add(index); 102 103 this._shiftAnchorIndex = NaN; 104 this._lastSelectedIndex = index; 105 106 this._updateSelectedItems(newSelectedItems); 107 } 108 109 deselectItem(index) 110 { 111 console.assert(index >= 0 && index < this.numberOfItems); 112 113 if (!this.hasSelectedItem(index)) 114 return; 115 116 if (!this._allowsEmptySelection && this._selectedIndexes.size === 1) 117 return; 118 119 let newSelectedItems = this._selectedIndexes.copy(); 120 newSelectedItems.delete(index); 121 122 if (this._shiftAnchorIndex === index) 123 this._shiftAnchorIndex = NaN; 124 125 if (this._lastSelectedIndex === index) { 126 this._lastSelectedIndex = NaN; 127 if (newSelectedItems.size) { 93 this._deselectAllAndSelect(item); 94 return; 95 } 96 97 this._lastSelectedItem = item; 98 this._shiftAnchorItem = null; 99 100 let newItems = new Set(extendSelection ? this._selectedItems : null); 101 newItems.add(item); 102 103 this._updateSelectedItems(newItems); 104 } 105 106 deselectItem(item) 107 { 108 console.assert(item, "Invalid item for selection."); 109 110 if (!this.hasSelectedItem(item)) 111 return; 112 113 if (!this._allowsEmptySelection && this._selectedItems.size === 1) 114 return; 115 116 let newItems = new Set(this._selectedItems); 117 newItems.delete(item); 118 119 if (this._lastSelectedItem === item) { 120 this._lastSelectedItem = null; 121 122 if (newItems.size) { 128 123 // Find selected item closest to deselected item. 129 let preceding = newSelectedItems.indexLessThan(index); 130 let following = newSelectedItems.indexGreaterThan(index); 131 132 if (isNaN(preceding)) 133 this._lastSelectedIndex = following; 134 else if (isNaN(following)) 135 this._lastSelectedIndex = preceding; 136 else { 137 if ((following - index) < (index - preceding)) 138 this._lastSelectedIndex = following; 139 else 140 this._lastSelectedIndex = preceding; 124 let previous = item; 125 let next = item; 126 while (!this._lastSelectedItem && previous && next) { 127 previous = this._previousSelectableItem(previous); 128 if (this.hasSelectedItem(previous)) { 129 this._lastSelectedItem = previous; 130 break; 131 } 132 133 next = this._nextSelectableItem(next); 134 if (this.hasSelectedItem(next)) { 135 this._lastSelectedItem = next; 136 break; 137 } 141 138 } 142 139 } 143 140 } 144 141 145 this._updateSelectedItems(newSelectedItems); 142 if (this._shiftAnchorItem === item) 143 this._shiftAnchorItem = null; 144 145 this._updateSelectedItems(newItems); 146 146 } 147 147 148 148 selectAll() 149 149 { 150 if (!this.numberOfItems || !this._allowsMultipleSelection) 151 return; 152 153 if (this._selectedIndexes.size === this.numberOfItems) 154 return; 155 156 let newSelectedItems = new WI.IndexSet; 157 newSelectedItems.addRange(0, this.numberOfItems); 158 159 this._lastSelectedIndex = newSelectedItems.lastIndex; 160 if (isNaN(this._shiftAnchorIndex)) 161 this._shiftAnchorIndex = this._lastSelectedIndex; 162 163 this._updateSelectedItems(newSelectedItems); 150 if (!this._allowsMultipleSelection) 151 return; 152 153 this._lastSelectedItem = this._lastSelectableItem(); 154 155 let newItems = new Set; 156 this._addRange(newItems, this._firstSelectableItem(), this._lastSelectedItem); 157 158 if (!this._shiftAnchorItem) 159 this._shiftAnchorItem = this._lastSelectedItem; 160 161 this._updateSelectedItems(newItems); 164 162 } 165 163 166 164 deselectAll() 167 165 { 168 const index = NaN; 169 this._deselectAllAndSelect(index); 166 this._deselectAllAndSelect(null); 170 167 } 171 168 172 169 removeSelectedItems() 173 170 { 174 let numberOfSelectedItems = this._selectedIndexes.size; 175 if (!numberOfSelectedItems) 176 return; 171 if (!this._selectedItems.size) 172 return; 173 174 let orderedSelection = Array.from(this._selectedItems).sort(this._comparator); 177 175 178 176 // Try selecting the item following the selection. 179 let lastSelectedI ndex = this._selectedIndexes.lastIndex;180 let i ndexToSelect = this._nextSelectableIndex(lastSelectedIndex);181 if ( isNaN(indexToSelect)) {177 let lastSelectedItem = orderedSelection.lastValue; 178 let itemToSelect = this._nextSelectableItem(lastSelectedItem); 179 if (!itemToSelect) { 182 180 // If no item exists after the last item in the selection, try selecting 183 181 // a deselected item (hole) within the selection. 184 let firstSelectedIndex = this._selectedIndexes.firstIndex; 185 if (lastSelectedIndex - firstSelectedIndex > numberOfSelectedItems) { 186 indexToSelect = this._nextSelectableIndex(firstSelectedIndex); 187 while (this._selectedIndexes.has(indexToSelect)) 188 indexToSelect = this._nextSelectableIndex(firstSelectedIndex); 189 } else { 182 itemToSelect = orderedSelection[0]; 183 while (itemToSelect && this.hasSelectedItem(itemToSelect)) 184 itemToSelect = this._nextSelectableItem(itemToSelect); 185 186 if (!itemToSelect || this.hasSelectedItem(itemToSelect)) { 190 187 // If the selection contains no holes, try selecting the item 191 188 // preceding the selection. 192 i ndexToSelect = firstSelectedIndex > 0 ? this._previousSelectableIndex(firstSelectedIndex) : NaN;189 itemToSelect = this._previousSelectableItem(orderedSelection[0]); 193 190 } 194 191 } 195 192 196 this._deselectAllAndSelect(i ndexToSelect);193 this._deselectAllAndSelect(itemToSelect); 197 194 } 198 195 199 196 reset() 200 197 { 201 this._shiftAnchorIndex = NaN; 202 this._lastSelectedIndex = NaN; 203 this._selectedIndexes.clear(); 204 } 205 206 didInsertItem(index) 207 { 208 let current = this._selectedIndexes.lastIndex; 209 while (current >= index) { 210 this._selectedIndexes.delete(current); 211 this._selectedIndexes.add(current + 1); 212 213 current = this._selectedIndexes.indexLessThan(current); 214 } 215 216 if (this._lastSelectedIndex >= index) 217 this._lastSelectedIndex += 1; 218 if (this._shiftAnchorIndex >= index) 219 this._shiftAnchorIndex += 1; 220 } 221 222 didRemoveItems(indexes) 223 { 224 if (!indexes) 225 return; 226 227 console.assert(indexes instanceof WI.IndexSet); 228 229 if (!indexes.size || !this._selectedIndexes.size) 230 return; 231 232 let firstRemovedIndex = indexes.firstIndex; 233 if (this._selectedIndexes.lastIndex < firstRemovedIndex) 234 return; 235 236 let newSelectedIndexes = new WI.IndexSet; 237 238 let lastRemovedIndex = indexes.lastIndex; 239 if (this._selectedIndexes.firstIndex < lastRemovedIndex) { 240 let removedCount = 0; 241 let removedIndex = firstRemovedIndex; 242 243 this._suppressSelectionDidChange = true; 244 245 // Adjust the selected indexes that are in the range between the 246 // first and last removed index (inclusive). 247 for (let current = this._selectedIndexes.firstIndex; current < lastRemovedIndex; current = this._selectedIndexes.indexGreaterThan(current)) { 248 if (this.hasSelectedItem(current)) { 249 this.deselectItem(current); 250 removedCount++; 251 continue; 252 } 253 254 while (removedIndex < current) { 255 removedCount++; 256 removedIndex = indexes.indexGreaterThan(removedIndex); 257 } 258 259 let newIndex = current - removedCount; 260 newSelectedIndexes.add(newIndex); 261 262 if (this._lastSelectedIndex === current) 263 this._lastSelectedIndex = newIndex; 264 if (this._shiftAnchorIndex === current) 265 this._shiftAnchorIndex = newIndex; 266 } 267 268 this._suppressSelectionDidChange = false; 269 } 270 271 let removedCount = indexes.size; 272 let current = lastRemovedIndex; 273 while (current = this._selectedIndexes.indexGreaterThan(current)) 274 newSelectedIndexes.add(current - removedCount); 275 276 if (this._lastSelectedIndex > lastRemovedIndex) 277 this._lastSelectedIndex -= removedCount; 278 if (this._shiftAnchorIndex > lastRemovedIndex) 279 this._shiftAnchorIndex -= removedCount; 280 281 this._selectedIndexes = newSelectedIndexes; 198 this._lastSelectedItem = null; 199 this._shiftAnchorItem = null; 200 this._selectedItems.clear(); 201 } 202 203 didRemoveItems(items) 204 { 205 console.assert(items instanceof Set); 206 207 if (!items.size || !this._selectedItems.size) 208 return; 209 210 this._updateSelectedItems(this._selectedItems.difference(items)); 282 211 } 283 212 284 213 handleKeyDown(event) 285 214 { 286 if (!this.numberOfItems)287 return false;288 289 215 if (event.key === "a" && event.commandOrControlKey) { 290 216 this.selectAll(); … … 306 232 } 307 233 308 handleItemMouseDown(index, event) 309 { 234 handleItemMouseDown(item, event) 235 { 236 console.assert(item, "Invalid item for selection."); 237 310 238 if (event.button !== 0 || event.ctrlKey) 311 239 return; … … 314 242 // whether or not multiple selection is enabled, so handle it first. 315 243 if (event.commandOrControlKey) { 316 if (this.hasSelectedItem(i ndex))317 this.deselectItem(i ndex);244 if (this.hasSelectedItem(item)) 245 this.deselectItem(item); 318 246 else 319 this.selectItem(i ndex, this._allowsMultipleSelection);247 this.selectItem(item, this._allowsMultipleSelection); 320 248 return; 321 249 } … … 323 251 let shiftExtendSelection = this._allowsMultipleSelection && event.shiftKey; 324 252 if (!shiftExtendSelection) { 325 this.selectItem(i ndex);326 return; 327 } 328 329 let new SelectedItems = this._selectedIndexes.copy();253 this.selectItem(item); 254 return; 255 } 256 257 let newItems = new Set(this._selectedItems); 330 258 331 259 // Shift-clicking when nothing is selected should cause the first item 332 260 // through the clicked item to be selected. 333 if (!newSelectedItems.size) { 334 this._shiftAnchorIndex = 0; 335 this._lastSelectedIndex = index; 336 newSelectedItems.addRange(0, index + 1); 337 this._updateSelectedItems(newSelectedItems); 338 return; 339 } 340 341 if (isNaN(this._shiftAnchorIndex)) 342 this._shiftAnchorIndex = this._lastSelectedIndex; 261 if (!newItems.size) { 262 this._lastSelectedItem = item; 263 this._shiftAnchorItem = this._firstSelectableItem(); 264 265 this._addRange(newItems, this._shiftAnchorItem, this._lastSelectedItem); 266 this._updateSelectedItems(newItems); 267 return; 268 } 269 270 if (!this._shiftAnchorItem) 271 this._shiftAnchorItem = this._lastSelectedItem; 343 272 344 273 // Shift-clicking will add to or delete from the current selection, or … … 348 277 // selected range and add the new range between the anchor and clicked item. 349 278 350 function normalizeRange(startIndex, endIndex){351 return startIndex > endIndex ? [endIndex, startIndex] : [startIndex, endIndex];352 } 353 354 if (this._shiftAnchorI ndex !== this._lastSelectedIndex) {355 let [startI ndex, endIndex] = normalizeRange(this._shiftAnchorIndex, this._lastSelectedIndex);356 newSelectedItems.deleteRange(startIndex, endIndex - startIndex + 1);357 } 358 359 let [startI ndex, endIndex] = normalizeRange(this._shiftAnchorIndex, index);360 newSelectedItems.addRange(startIndex, endIndex - startIndex + 1);361 362 this._lastSelectedI ndex = index;363 364 this._updateSelectedItems(new SelectedItems);279 let sortItemPair = (a, b) => { 280 return [a, b].sort(this._comparator); 281 }; 282 283 if (this._shiftAnchorItem !== this._lastSelectedItem) { 284 let [startItem, endItem] = sortItemPair(this._shiftAnchorItem, this._lastSelectedItem); 285 this._deleteRange(newItems, startItem, endItem); 286 } 287 288 let [startItem, endItem] = sortItemPair(this._shiftAnchorItem, item); 289 this._addRange(newItems, startItem, endItem); 290 291 this._lastSelectedItem = item; 292 293 this._updateSelectedItems(newItems); 365 294 } 366 295 367 296 // Private 368 297 369 _deselectAllAndSelect(i ndex)370 { 371 if (!this._selectedI ndexes.size)372 return; 373 374 if (this._selectedI ndexes.size === 1 && this._selectedIndexes.firstIndex === index)375 return; 376 377 this._ shiftAnchorIndex = NaN;378 this._ lastSelectedIndex = index;379 380 let new SelectedItems = new WI.IndexSet;381 if ( !isNaN(index))382 new SelectedItems.add(index);383 384 this._updateSelectedItems(new SelectedItems);298 _deselectAllAndSelect(item) 299 { 300 if (!this._selectedItems.size) 301 return; 302 303 if (this._selectedItems.size === 1 && this.hasSelectedItem(item)) 304 return; 305 306 this._lastSelectedItem = item; 307 this._shiftAnchorItem = null; 308 309 let newItems = new Set; 310 if (item) 311 newItems.add(item); 312 313 this._updateSelectedItems(newItems); 385 314 } 386 315 387 316 _selectItemsFromArrowKey(goingUp, shiftKey) 388 317 { 389 if (!this._selectedIndexes.size) { 390 let index = goingUp ? this.numberOfItems - 1 : 0; 391 this.selectItem(index); 392 return; 393 } 394 395 let index = goingUp ? this._previousSelectableIndex(this._lastSelectedIndex) : this._nextSelectableIndex(this._lastSelectedIndex); 396 if (isNaN(index)) 318 if (!this._selectedItems.size) { 319 this.selectItem(goingUp ? this._lastSelectableItem() : this._firstSelectableItem()); 320 return; 321 } 322 323 let item = goingUp ? this._previousSelectableItem(this._lastSelectedItem) : this._nextSelectableItem(this._lastSelectedItem); 324 if (!item) 397 325 return; 398 326 399 327 let extendSelection = shiftKey && this._allowsMultipleSelection; 400 if (!extendSelection || !this.hasSelectedItem(i ndex)) {401 this.selectItem(i ndex, extendSelection);328 if (!extendSelection || !this.hasSelectedItem(item)) { 329 this.selectItem(item, extendSelection); 402 330 return; 403 331 } … … 406 334 // extending the selection into the item, or deselecting. Determine which 407 335 // by checking whether the item opposite the anchor item is selected. 408 let priorI ndex = goingUp ? this._nextSelectableIndex(this._lastSelectedIndex) : this._previousSelectableIndex(this._lastSelectedIndex);409 if (! this.hasSelectedItem(priorIndex)) {410 this.deselectItem(this._lastSelectedI ndex);336 let priorItem = goingUp ? this._nextSelectableItem(this._lastSelectedItem) : this._previousSelectableItem(this._lastSelectedItem); 337 if (!priorItem || !this.hasSelectedItem(priorItem)) { 338 this.deselectItem(this._lastSelectedItem); 411 339 return; 412 340 } … … 415 343 // anchor item then continue searching in the direction of movement 416 344 // for an unselected item to select. 417 while ( !isNaN(index)) {418 if (!this.hasSelectedItem(i ndex)) {419 this.selectItem(i ndex, extendSelection);345 while (item) { 346 if (!this.hasSelectedItem(item)) { 347 this.selectItem(item, extendSelection); 420 348 break; 421 349 } 422 350 423 this._lastSelectedIndex = index; 424 index = goingUp ? this._previousSelectableIndex(index) : this._nextSelectableIndex(index); 425 } 426 } 427 428 _nextSelectableIndex(index) 429 { 430 return this._delegate.selectionControllerNextSelectableIndex(this, index); 431 } 432 433 _previousSelectableIndex(index) 434 { 435 return this._delegate.selectionControllerPreviousSelectableIndex(this, index); 436 } 437 438 _updateSelectedItems(indexes) 439 { 440 if (this._selectedIndexes.equals(indexes)) 441 return; 442 443 let oldSelectedIndexes = this._selectedIndexes.copy(); 444 this._selectedIndexes = indexes; 351 this._lastSelectedItem = item; 352 item = goingUp ? this._previousSelectableItem(item) : this._nextSelectableItem(item); 353 } 354 } 355 356 _firstSelectableItem() 357 { 358 return this._delegate.selectionControllerFirstSelectableItem(this); 359 } 360 361 _lastSelectableItem() 362 { 363 return this._delegate.selectionControllerLastSelectableItem(this); 364 } 365 366 _previousSelectableItem(item) 367 { 368 return this._delegate.selectionControllerPreviousSelectableItem(this, item); 369 } 370 371 _nextSelectableItem(item) 372 { 373 return this._delegate.selectionControllerNextSelectableItem(this, item); 374 } 375 376 _updateSelectedItems(items) 377 { 378 let oldSelectedItems = this._selectedItems; 379 this._selectedItems = items; 445 380 446 381 if (this._suppressSelectionDidChange || !this._delegate.selectionControllerSelectionDidChange) 447 382 return; 448 383 449 let deselectedItems = oldSelectedIndexes.difference(indexes); 450 let selectedItems = indexes.difference(oldSelectedIndexes); 451 this._delegate.selectionControllerSelectionDidChange(this, deselectedItems, selectedItems); 384 let deselectedItems = oldSelectedItems.difference(items); 385 let selectedItems = items.difference(oldSelectedItems); 386 if (deselectedItems.size || selectedItems.size) 387 this._delegate.selectionControllerSelectionDidChange(this, deselectedItems, selectedItems); 388 } 389 390 _addRange(items, firstItem, lastItem) 391 { 392 let current = firstItem; 393 while (current) { 394 items.add(current); 395 if (current === lastItem) 396 break; 397 current = this._nextSelectableItem(current); 398 } 399 400 console.assert(!lastItem || items.has(lastItem), "End of range could not be reached."); 401 } 402 403 _deleteRange(items, firstItem, lastItem) 404 { 405 let current = firstItem; 406 while (current) { 407 items.delete(current); 408 if (current === lastItem) 409 break; 410 current = this._nextSelectableItem(current); 411 } 412 413 console.assert(!lastItem || !items.has(lastItem), "End of range could not be reached."); 452 414 } 453 415 }; -
trunk/Source/WebInspectorUI/UserInterface/Main.html
r241325 r241652 277 277 <script src="Base/Platform.js"></script> 278 278 <script src="Base/DebuggableType.js"></script> 279 <script src="Base/IndexSet.js"></script>280 279 <script src="Base/LinkedList.js"></script> 281 280 <script src="Base/ListMultimap.js"></script> -
trunk/Source/WebInspectorUI/UserInterface/Test.html
r240457 r241652 39 39 <script src="Base/Platform.js"></script> 40 40 <script src="Base/DebuggableType.js"></script> 41 <script src="Base/IndexSet.js"></script>42 41 <script src="Base/LinkedList.js"></script> 43 42 <script src="Base/ListMultimap.js"></script> -
trunk/Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js
r239226 r241652 76 76 // Table dataSource 77 77 78 tableIndexForRepresentedObject(table, object) 79 { 80 let index = this._cookies.indexOf(object); 81 console.assert(index >= 0); 82 return index; 83 } 84 85 tableRepresentedObjectForIndex(table, index) 86 { 87 console.assert(index >= 0 && index < this._cookies.length); 88 return this._cookies[index]; 89 } 90 78 91 tableNumberOfRows(table) 79 92 { -
trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js
r240639 r241652 320 320 } 321 321 322 // Protected 323 324 objectForSelection(treeElement) 325 { 326 if (treeElement instanceof WI.DOMTreeElement && treeElement.isCloseTag()) { 327 // SelectionController requires every selectable item to be unique. 328 // The DOMTreeElement for a close tag has the same represented object 329 // as it's parent (the open tag). Return a proxy object associated 330 // with the tree element for the close tag so it can be selected. 331 if (!treeElement.__closeTagProxyObject) 332 treeElement.__closeTagProxyObject = {__proxyObjectTreeElement: treeElement}; 333 return treeElement.__closeTagProxyObject; 334 } 335 336 return super.objectForSelection(treeElement); 337 } 338 322 339 // Private 323 340 -
trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTableContentView.js
r241633 r241652 328 328 // Table dataSource 329 329 330 tableIndexForRepresentedObject(table, object) 331 { 332 return this._filteredEntries.indexOf(object); 333 } 334 335 tableRepresentedObjectForIndex(table, index) 336 { 337 console.assert(index >=0 && index < this._filteredEntries.length); 338 return this._filteredEntries[index]; 339 } 340 330 341 tableNumberOfRows(table) 331 342 { -
trunk/Source/WebInspectorUI/UserInterface/Views/Table.js
r240347 r241652 88 88 this._fillerHeight = 0; // Calculated in _resizeColumnsAndFiller. 89 89 90 this._selectionController = new WI.SelectionController(this );90 this._selectionController = new WI.SelectionController(this, (a, b) => this._indexForRepresentedObject(a) - this._indexForRepresentedObject(b)); 91 91 92 92 this._resizers = []; … … 114 114 this._visibleRowIndexEnd = NaN; 115 115 116 console.assert(this._dataSource.tableIndexForRepresentedObject, "Table data source must implement tableIndexForRepresentedObject."); 117 console.assert(this._dataSource.tableRepresentedObjectForIndex, "Table data source must implement tableRepresentedObjectForIndex."); 118 116 119 console.assert(this._delegate.tablePopulateCell, "Table delegate must implement tablePopulateCell."); 117 120 } … … 126 129 get selectedRow() 127 130 { 128 return this._selectionController.lastSelectedItem; 131 let item = this._selectionController.lastSelectedItem; 132 let index = this._indexForRepresentedObject(item); 133 return index >= 0 ? index : NaN; 129 134 } 130 135 131 136 get selectedRows() 132 137 { 133 return Array.from(this._selectionController.selectedItems); 138 let rowIndexes = []; 139 for (let item of this._selectionController.selectedItems) 140 rowIndexes.push(this._indexForRepresentedObject(item)); 141 return rowIndexes; 134 142 } 135 143 … … 237 245 isRowSelected(rowIndex) 238 246 { 239 return this._selectionController.hasSelectedItem( rowIndex);247 return this._selectionController.hasSelectedItem(this._representedObjectForIndex(rowIndex)); 240 248 } 241 249 … … 314 322 selectRow(rowIndex, extendSelection = false) 315 323 { 316 this._selectionController.selectItem( rowIndex, extendSelection);324 this._selectionController.selectItem(this._representedObjectForIndex(rowIndex), extendSelection); 317 325 } 318 326 319 327 deselectRow(rowIndex) 320 328 { 321 this._selectionController.deselectItem( rowIndex);329 this._selectionController.deselectItem(this._representedObjectForIndex(rowIndex)); 322 330 } 323 331 … … 339 347 this.deselectRow(rowIndex); 340 348 341 let rowIndexes = new WI.IndexSet([rowIndex]); 342 this._removeRows(rowIndexes); 349 this._removeRows(new Set([this._representedObjectForIndex(rowIndex)])); 343 350 } 344 351 345 352 removeSelectedRows() 346 353 { 354 let selectedItems = this._selectionController.selectedItems; 355 if (!selectedItems.size) 356 return; 357 347 358 // Change the selection before removing rows. This matches the behavior 348 359 // of macOS Finder (in list and column modes) when removing selected items. 349 let oldSelectedItems = this._selectionController.selectedItems.copy();350 351 360 this._selectionController.removeSelectedItems(); 352 361 353 if (!oldSelectedItems.equals(this._selectionController.selectedItems)) 354 this._removeRows(oldSelectedItems); 362 this._removeRows(selectedItems); 355 363 } 356 364 … … 598 606 selectionControllerSelectionDidChange(controller, deselectedItems, selectedItems) 599 607 { 600 if (deselectedItems.size) 601 this._toggleSelectedRowStyle(deselectedItems, false); 602 if (selectedItems.size) 603 this._toggleSelectedRowStyle(selectedItems, true); 608 for (let item of deselectedItems) { 609 let rowIndex = this._indexForRepresentedObject(item); 610 let row = this._cachedRows.get(rowIndex); 611 if (row) 612 row.classList.toggle("selected", false); 613 } 614 615 for (let item of selectedItems) { 616 let rowIndex = this._indexForRepresentedObject(item); 617 let row = this._cachedRows.get(rowIndex); 618 if (row) 619 row.classList.toggle("selected", true); 620 } 604 621 605 622 if (selectedItems.size === 1) { 606 let rowIndex = selectedItems.firstIndex;623 let rowIndex = this._indexForRepresentedObject(selectedItems.firstValue); 607 624 if (!this._isRowVisible(rowIndex)) 608 625 this.revealRow(rowIndex); … … 613 630 } 614 631 615 selectionControllerNumberOfItems(controller) 616 { 617 return this.numberOfRows; 618 } 619 620 selectionControllerNextSelectableIndex(controller, index) 621 { 622 if (index >= this.numberOfRows - 1) 623 return NaN; 624 return index + 1; 625 } 626 627 selectionControllerPreviousSelectableIndex(controller, index) 628 { 629 if (index <= 0) 630 return NaN; 631 return index - 1; 632 selectionControllerFirstSelectableItem(controller) 633 { 634 return this._representedObjectForIndex(0); 635 } 636 637 selectionControllerLastSelectableItem(controller) 638 { 639 return this._representedObjectForIndex(this.numberOfRows - 1); 640 } 641 642 selectionControllerPreviousSelectableItem(controller, item) 643 { 644 let index = this._indexForRepresentedObject(item); 645 console.assert(index >= 0 && index < this.numberOfRows); 646 647 return index > 0 ? this._representedObjectForIndex(index - 1) : null; 648 } 649 650 selectionControllerNextSelectableItem(controller, item) 651 { 652 let index = this._indexForRepresentedObject(item); 653 console.assert(index >= 0 && index < this.numberOfRows); 654 655 return index < this.numberOfRows - 1 ? this._representedObjectForIndex(index + 1) : null; 632 656 } 633 657 … … 1296 1320 } 1297 1321 1298 this._selectionController.handleItemMouseDown( rowIndex, event);1322 this._selectionController.handleItemMouseDown(this._representedObjectForIndex(rowIndex), event); 1299 1323 } 1300 1324 … … 1375 1399 } 1376 1400 1377 _removeRows(r owIndexes)1401 _removeRows(representedObjects) 1378 1402 { 1379 1403 let removed = 0; … … 1388 1412 }; 1389 1413 1390 for (let index = rowIndexes.firstIndex; index <= rowIndexes.lastIndex; ++index) { 1391 if (rowIndexes.has(index)) { 1414 let rowIndexes = []; 1415 for (let object of representedObjects) 1416 rowIndexes.push(this._indexForRepresentedObject(object)); 1417 1418 rowIndexes.sort((a, b) => a - b); 1419 1420 let lastIndex = rowIndexes.lastValue; 1421 for (let index = rowIndexes[0]; index <= lastIndex; ++index) { 1422 if (rowIndexes.binaryIndexOf(index) >= 0) { 1392 1423 let row = this._cachedRows.get(index); 1393 1424 if (row) { … … 1406 1437 return; 1407 1438 1408 for (let index = rowIndexes.lastIndex + 1; index < this._cachedNumberOfRows; ++index)1439 for (let index = lastIndex + 1; index < this._cachedNumberOfRows; ++index) 1409 1440 adjustRowAtIndex(index); 1410 1441 … … 1412 1443 console.assert(this._cachedNumberOfRows >= 0); 1413 1444 1414 this._selectionController.didRemoveItems(r owIndexes);1445 this._selectionController.didRemoveItems(representedObjects); 1415 1446 1416 1447 if (this._delegate.tableDidRemoveRows) { 1417 this._delegate.tableDidRemoveRows(this, Array.from(rowIndexes));1448 this._delegate.tableDidRemoveRows(this, rowIndexes); 1418 1449 console.assert(this._cachedNumberOfRows === this._dataSource.tableNumberOfRows(this), "Table data source should update after removing rows."); 1419 1450 } 1420 1451 } 1421 1452 1422 _toggleSelectedRowStyle(rowIndexes, flag) 1423 { 1424 for (let index of rowIndexes) { 1425 let row = this._cachedRows.get(index); 1426 if (row) 1427 row.classList.toggle("selected", flag); 1428 } 1453 _indexForRepresentedObject(object) 1454 { 1455 return this.dataSource.tableIndexForRepresentedObject(this, object); 1456 } 1457 1458 _representedObjectForIndex(index) 1459 { 1460 return this.dataSource.tableRepresentedObjectForIndex(this, index); 1429 1461 } 1430 1462 }; -
trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js
r241003 r241652 57 57 this._cachedNumberOfDescendents = 0; 58 58 this._previousSelectedTreeElement = null; 59 this._selectionController = new WI.SelectionController(this); 59 60 let comparator = (a, b) => { 61 function getLevel(treeElement) { 62 let level = 0; 63 while (treeElement = treeElement.parent) 64 level++; 65 return level; 66 } 67 68 function compareSiblings(s, t) { 69 return s.parent.children.indexOf(s) - s.parent.children.indexOf(t); 70 } 71 72 // Translate represented objects to TreeElements, which have the 73 // hierarchical information needed to perform the comparison. 74 a = this.getCachedTreeElement(a); 75 b = this.getCachedTreeElement(b); 76 if (!a || !b) 77 return 0; 78 79 if (a.parent === b.parent) 80 return compareSiblings(a, b); 81 82 let aLevel = getLevel(a); 83 let bLevel = getLevel(b); 84 while (aLevel > bLevel) { 85 if (a.parent === b) 86 return 1; 87 a = a.parent; 88 aLevel--; 89 } 90 while (bLevel > aLevel) { 91 if (b.parent === a) 92 return -1; 93 b = b.parent; 94 bLevel--; 95 } 96 97 while (a.parent !== b.parent) { 98 a = a.parent; 99 b = b.parent; 100 } 101 102 console.assert(a.parent === b.parent, "Missing common ancestor for TreeElements.", a, b); 103 return compareSiblings(a, b); 104 }; 105 106 this._selectionController = new WI.SelectionController(this, comparator); 60 107 61 108 this._itemWasSelectedByUser = false; … … 104 151 get selectedTreeElement() 105 152 { 106 let selectedIndex = this._selectionController.lastSelectedItem; 107 return this._treeElementAtIndex(selectedIndex) || null; 153 return this.getCachedTreeElement(this._selectionController.lastSelectedItem); 108 154 } 109 155 110 156 set selectedTreeElement(treeElement) 111 157 { 112 if (treeElement) { 113 let index = this._indexOfTreeElement(treeElement); 114 this._selectionController.selectItem(index); 115 } else 158 if (treeElement) 159 this._selectionController.selectItem(this.objectForSelection(treeElement)); 160 else 116 161 this._selectionController.deselectAll(); 117 162 } … … 121 166 if (this.allowsMultipleSelection) { 122 167 let treeElements = []; 123 for (let indexof this._selectionController.selectedItems)124 treeElements.push(this. _treeElementAtIndex(index));168 for (let representedObject of this._selectionController.selectedItems) 169 treeElements.push(this.getCachedTreeElement(representedObject)); 125 170 return treeElements; 126 171 } … … 324 369 } 325 370 326 let removedIndexes = null;327 328 371 let treeOutline = child.treeOutline; 329 372 if (treeOutline) { 330 373 treeOutline._forgetTreeElement(child); 331 374 treeOutline._forgetChildrenRecursive(child); 332 removedIndexes = treeOutline._indexesForSubtree(child);333 375 } 334 376 … … 346 388 child.previousSibling = null; 347 389 348 if (treeOutline) { 349 treeOutline._selectionController.didRemoveItems(removedIndexes); 390 if (treeOutline) 350 391 treeOutline.dispatchEventToListeners(WI.TreeOutline.Event.ElementRemoved, {element: child}); 351 }352 392 } 353 393 … … 384 424 } 385 425 386 let removedIndexes = treeOutline._indexesForSubtree(child);387 388 426 child._detach(); 389 427 child.treeOutline = null; … … 394 432 this.children.shift(); 395 433 396 if (treeOutline) { 397 treeOutline._selectionController.didRemoveItems(removedIndexes); 434 if (treeOutline) 398 435 treeOutline.dispatchEventToListeners(WI.TreeOutline.Event.ElementRemoved, {element: child}); 399 }400 436 } 401 437 } … … 414 450 elements.push(element); 415 451 this._cachedNumberOfDescendents++; 416 417 let index = this._indexOfTreeElement(element);418 if (index >= 0) {419 console.assert(!element.selected, "TreeElement should not be selected before being inserted.");420 this._selectionController.didInsertItem(index);421 }422 452 } 423 453 … … 447 477 if (!representedObject) 448 478 return null; 479 480 // SelectionController requires every selectable object to be unique. 481 // A TreeOutline subclass where multiple TreeElements may be associated 482 // with one represented object can override objectForSelection, and return 483 // a proxy object that is associated with a single TreeElement. 484 if (representedObject.__proxyObjectTreeElement) 485 return representedObject.__proxyObjectTreeElement; 449 486 450 487 if (representedObject.__treeElementIdentifier) { … … 807 844 this._processingSelectionChange = true; 808 845 809 for (let index of deselectedItems) { 810 let treeElement = this._treeElementAtIndex(index); 811 console.assert(treeElement, "Missing TreeElement for deselected index " + index); 812 if (treeElement) { 813 if (treeElement.listItemElement) 814 treeElement.listItemElement.classList.remove("selected"); 815 treeElement.deselect(); 816 } 817 } 818 819 for (let index of selectedItems) { 820 let treeElement = this._treeElementAtIndex(index); 821 console.assert(treeElement, "Missing TreeElement for selected index " + index); 822 if (treeElement) { 823 if (treeElement.listItemElement) 824 treeElement.listItemElement.classList.add("selected"); 825 const omitFocus = true; 826 treeElement.select(omitFocus); 827 } 846 for (let representedObject of deselectedItems) { 847 let treeElement = this.getCachedTreeElement(representedObject); 848 if (!treeElement) 849 continue; 850 851 if (treeElement.listItemElement) 852 treeElement.listItemElement.classList.remove("selected"); 853 854 treeElement.deselect(); 855 } 856 857 for (let representedObject of selectedItems) { 858 let treeElement = this.getCachedTreeElement(representedObject); 859 if (!treeElement) 860 continue; 861 862 if (treeElement.listItemElement) 863 treeElement.listItemElement.classList.add("selected"); 864 865 const omitFocus = true; 866 treeElement.select(omitFocus); 828 867 } 829 868 … … 844 883 } 845 884 846 selectionControllerNextSelectableIndex(controller, index) 847 { 848 let treeElement = this._treeElementAtIndex(index); 885 selectionControllerFirstSelectableItem(controller) 886 { 887 let firstChild = this.children[0]; 888 if (firstChild.selectable) 889 return firstChild.representedObject; 890 return this.selectionControllerNextSelectableItem(controller, firstChild.representedObject); 891 } 892 893 selectionControllerLastSelectableItem(controller) 894 { 895 let treeElement = this.children.lastValue; 896 while (treeElement.expanded && treeElement.children.length) 897 treeElement = treeElement.children.lastValue; 898 899 let item = this.objectForSelection(treeElement); 900 if (treeElement.selectable) 901 return item; 902 return this.selectionControllerPreviousSelectableItem(controller, item); 903 } 904 905 selectionControllerPreviousSelectableItem(controller, item) 906 { 907 let treeElement = this.getCachedTreeElement(item); 908 console.assert(treeElement, "Missing TreeElement for representedObject.", item); 849 909 if (!treeElement) 850 return NaN;910 return null; 851 911 852 912 const skipUnrevealed = true; … … 854 914 const dontPopulate = true; 855 915 856 while (treeElement = treeElement.traverse NextTreeElement(skipUnrevealed, stayWithin, dontPopulate)) {916 while (treeElement = treeElement.traversePreviousTreeElement(skipUnrevealed, stayWithin, dontPopulate)) { 857 917 if (treeElement.selectable) 858 return this._indexOfTreeElement(treeElement); 859 } 860 861 return NaN; 862 } 863 864 selectionControllerPreviousSelectableIndex(controller, index) 865 { 866 let treeElement = this._treeElementAtIndex(index); 918 return this.objectForSelection(treeElement); 919 } 920 921 return null; 922 } 923 924 selectionControllerNextSelectableItem(controller, item) 925 { 926 let treeElement = this.getCachedTreeElement(item); 927 console.assert(treeElement, "Missing TreeElement for representedObject.", item); 867 928 if (!treeElement) 868 return NaN;929 return null; 869 930 870 931 const skipUnrevealed = true; … … 872 933 const dontPopulate = true; 873 934 874 while (treeElement = treeElement.traverse PreviousTreeElement(skipUnrevealed, stayWithin, dontPopulate)) {935 while (treeElement = treeElement.traverseNextTreeElement(skipUnrevealed, stayWithin, dontPopulate)) { 875 936 if (treeElement.selectable) 876 return this. _indexOfTreeElement(treeElement);877 } 878 879 return NaN;937 return this.objectForSelection(treeElement); 938 } 939 940 return null; 880 941 } 881 942 882 943 // Protected 944 945 objectForSelection(treeElement) 946 { 947 return treeElement.representedObject; 948 } 883 949 884 950 selectTreeElementInternal(treeElement, suppressNotification = false, selectedByUser = false) … … 1023 1089 } 1024 1090 1025 let index = this._indexOfTreeElement(treeElement);1026 if (isNaN(index))1027 return;1028 1029 1091 this._itemWasSelectedByUser = true; 1030 this._selectionController.handleItemMouseDown( index, event);1092 this._selectionController.handleItemMouseDown(this.objectForSelection(treeElement), event); 1031 1093 this._itemWasSelectedByUser = false; 1032 }1033 1034 _indexOfTreeElement(treeElement)1035 {1036 const skipUnrevealed = false;1037 const stayWithin = null;1038 const dontPopulate = true;1039 1040 let index = 0;1041 let current = this.children[0];1042 while (current) {1043 if (treeElement === current)1044 return index;1045 1046 current = current.traverseNextTreeElement(skipUnrevealed, stayWithin, dontPopulate);1047 ++index;1048 }1049 1050 console.assert(false, "Unable to get index for tree element.", treeElement);1051 return NaN;1052 }1053 1054 _treeElementAtIndex(index)1055 {1056 const skipUnrevealed = false;1057 const stayWithin = null;1058 const dontPopulate = true;1059 1060 let current = 0;1061 let treeElement = this.children[0];1062 while (treeElement) {1063 if (current === index)1064 return treeElement;1065 1066 treeElement = treeElement.traverseNextTreeElement(skipUnrevealed, stayWithin, dontPopulate);1067 ++current;1068 }1069 1070 return null;1071 1094 } 1072 1095 … … 1082 1105 1083 1106 this.dispatchEventToListeners(WI.TreeOutline.Event.SelectionDidChange, {selectedByUser}); 1084 }1085 1086 _indexesForSubtree(treeElement)1087 {1088 let treeOutline = treeElement.treeOutline;1089 if (!treeOutline)1090 return null;1091 1092 function numberOfElementsInSubtree(treeElement) {1093 let elements = treeElement.root ? Array.from(treeElement.children) : [treeElement];1094 let count = 0;1095 while (elements.length) {1096 let child = elements.pop();1097 if (child.hidden)1098 continue;1099 1100 count++;1101 elements = elements.concat(child.children);1102 }1103 return count;1104 }1105 1106 let firstChild = treeElement.root ? treeElement.children[0] : treeElement;1107 let startIndex = treeOutline._indexOfTreeElement(firstChild);1108 let count = numberOfElementsInSubtree(treeElement);1109 let indexes = new WI.IndexSet;1110 indexes.addRange(startIndex, count);1111 return indexes;1112 1107 } 1113 1108 };
Note: See TracChangeset
for help on using the changeset viewer.