Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/ui/treeoutline.js

Issue 2644233007: DevTools: Use real focus in TreeOutline (Closed)
Patch Set: changes Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2007 Apple Inc. All rights reserved. 2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 15 matching lines...) Expand all
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29 /** 29 /**
30 * @unrestricted 30 * @unrestricted
31 */ 31 */
32 UI.TreeOutline = class extends Common.Object { 32 UI.TreeOutline = class extends Common.Object {
33 /** 33 /**
34 * @param {boolean=} nonFocusable 34 * @param {boolean=} nonFocusable
35 */ 35 */
36 constructor(nonFocusable) { 36 constructor(nonFocusable) {
pfeldman 2017/01/30 18:43:07 I'd remove this and only leave the setFocusable fo
einbinder 2017/02/01 22:51:35 Done.
37 super(); 37 super();
38 this._createRootElement(); 38 this._createRootElement();
39 39
40 /** @type {?UI.TreeElement} */
40 this.selectedTreeElement = null; 41 this.selectedTreeElement = null;
41 this.expandTreeElementsWhenArrowing = false; 42 this.expandTreeElementsWhenArrowing = false;
42 /** @type {?function(!UI.TreeElement, !UI.TreeElement):number} */ 43 /** @type {?function(!UI.TreeElement, !UI.TreeElement):number} */
43 this._comparator = null; 44 this._comparator = null;
44 45
45 this.contentElement = this._rootElement._childrenListNode; 46 this.contentElement = this._rootElement._childrenListNode;
46 this.contentElement.addEventListener('keydown', this._treeKeyDown.bind(this) , true); 47 this.contentElement.addEventListener('keydown', this._treeKeyDown.bind(this) , true);
47 this.contentElement.addEventListener('focus', setFocused.bind(this, true), f alse);
48 this.contentElement.addEventListener('blur', setFocused.bind(this, false), f alse);
49 48
50 this.setFocusable(!nonFocusable); 49 this._focusable = !nonFocusable;
51 50
52 this.element = this.contentElement; 51 this.element = this.contentElement;
53 52
54 // Adjust to allow computing margin-left for the selection element. 53 // Adjust to allow computing margin-left for the selection element.
55 // Check the padding-left for the li element for correct value. 54 // Check the padding-left for the li element for correct value.
56 this._paddingSize = 0; 55 this._paddingSize = 0;
57
58 /**
59 * @param {boolean} isFocused
60 * @this {UI.TreeOutline}
61 */
62 function setFocused(isFocused) {
63 this._focused = isFocused;
64 if (this.selectedTreeElement)
65 this.selectedTreeElement._setFocused(this._focused);
66 }
67 } 56 }
68 57
69 _createRootElement() { 58 _createRootElement() {
70 this._rootElement = new UI.TreeElement(); 59 this._rootElement = new UI.TreeElement();
71 this._rootElement.treeOutline = this; 60 this._rootElement.treeOutline = this;
72 this._rootElement.root = true; 61 this._rootElement.root = true;
73 this._rootElement.selectable = false; 62 this._rootElement.selectable = false;
74 this._rootElement.expanded = true; 63 this._rootElement.expanded = true;
75 this._rootElement._childrenListNode.classList.remove('children'); 64 this._rootElement._childrenListNode.classList.remove('children');
76 } 65 }
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 return event ? this.treeElementFromPoint(event.pageX, event.pageY) : null; 128 return event ? this.treeElementFromPoint(event.pageX, event.pageY) : null;
140 } 129 }
141 130
142 /** 131 /**
143 * @param {?function(!UI.TreeElement, !UI.TreeElement):number} comparator 132 * @param {?function(!UI.TreeElement, !UI.TreeElement):number} comparator
144 */ 133 */
145 setComparator(comparator) { 134 setComparator(comparator) {
146 this._comparator = comparator; 135 this._comparator = comparator;
147 } 136 }
148 137
149 /**
150 * @param {boolean} focusable
151 */
152 setFocusable(focusable) {
153 if (focusable)
154 this.contentElement.setAttribute('tabIndex', 0);
155 else
156 this.contentElement.removeAttribute('tabIndex');
157 }
158
159 focus() { 138 focus() {
160 this.contentElement.focus(); 139 if (this.selectedTreeElement)
pfeldman 2017/01/30 18:43:07 What if there is no selected element? Can there be
einbinder 2017/02/01 22:51:35 Selecting the contentElement now, and restoring fo
140 this.selectedTreeElement.listItemElement.focus();
161 } 141 }
162 142
163 /** 143 /**
164 * @param {!UI.TreeElement} element 144 * @param {!UI.TreeElement} element
165 */ 145 */
166 _bindTreeElement(element) { 146 _bindTreeElement(element) {
167 if (element.treeOutline) 147 if (element.treeOutline)
168 console.error('Binding element for the second time: ' + new Error().stack) ; 148 console.error('Binding element for the second time: ' + new Error().stack) ;
169 element.treeOutline = this; 149 element.treeOutline = this;
170 element.onbind(); 150 element.onbind();
171 } 151 }
172 152
173 /** 153 /**
174 * @param {!UI.TreeElement} element 154 * @param {!UI.TreeElement} element
175 */ 155 */
176 _unbindTreeElement(element) { 156 _unbindTreeElement(element) {
177 if (!element.treeOutline) 157 if (!element.treeOutline)
178 console.error('Unbinding element that was not bound: ' + new Error().stack ); 158 console.error('Unbinding element that was not bound: ' + new Error().stack );
179 159
180 element.deselect(); 160 element.deselect();
181 element.onunbind(); 161 element.onunbind();
182 element.treeOutline = null; 162 element.treeOutline = null;
163 element.listItemElement.removeAttribute('tabIndex');
pfeldman 2017/01/30 18:43:07 It seems like you are now balancing this in the se
einbinder 2017/02/01 22:51:35 Yep, that is better.
183 } 164 }
184 165
185 /** 166 /**
186 * @return {boolean} 167 * @return {boolean}
187 */ 168 */
188 selectPrevious() { 169 selectPrevious() {
189 var nextSelectedElement = this.selectedTreeElement.traversePreviousTreeEleme nt(true); 170 var nextSelectedElement = this.selectedTreeElement.traversePreviousTreeEleme nt(true);
190 while (nextSelectedElement && !nextSelectedElement.selectable) 171 while (nextSelectedElement && !nextSelectedElement.selectable)
191 nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!thi s.expandTreeElementsWhenArrowing); 172 nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!thi s.expandTreeElementsWhenArrowing);
192 if (nextSelectedElement) { 173 if (nextSelectedElement) {
(...skipping 23 matching lines...) Expand all
216 * @param {number} paddingSize 197 * @param {number} paddingSize
217 */ 198 */
218 setPaddingSize(paddingSize) { 199 setPaddingSize(paddingSize) {
219 this._paddingSize = paddingSize; 200 this._paddingSize = paddingSize;
220 } 201 }
221 202
222 /** 203 /**
223 * @param {!Event} event 204 * @param {!Event} event
224 */ 205 */
225 _treeKeyDown(event) { 206 _treeKeyDown(event) {
226 if (event.target !== this.contentElement) 207 if (!this.selectedTreeElement || event.target !== this.selectedTreeElement.l istItemElement || event.shiftKey ||
227 return; 208 event.metaKey || event.ctrlKey)
228
229 if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ct rlKey)
230 return; 209 return;
231 210
232 var handled = false; 211 var handled = false;
233 if (event.key === 'ArrowUp' && !event.altKey) { 212 if (event.key === 'ArrowUp' && !event.altKey) {
234 handled = this.selectPrevious(); 213 handled = this.selectPrevious();
235 } else if (event.key === 'ArrowDown' && !event.altKey) { 214 } else if (event.key === 'ArrowDown' && !event.altKey) {
236 handled = this.selectNext(); 215 handled = this.selectNext();
237 } else if (event.key === 'ArrowLeft') { 216 } else if (event.key === 'ArrowLeft') {
238 handled = this.selectedTreeElement.collapseOrAscend(event.altKey); 217 handled = this.selectedTreeElement.collapseOrAscend(event.altKey);
239 } else if (event.key === 'ArrowRight') { 218 } else if (event.key === 'ArrowRight') {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 ElementAttached: Symbol('ElementAttached'), 259 ElementAttached: Symbol('ElementAttached'),
281 ElementExpanded: Symbol('ElementExpanded'), 260 ElementExpanded: Symbol('ElementExpanded'),
282 ElementCollapsed: Symbol('ElementCollapsed'), 261 ElementCollapsed: Symbol('ElementCollapsed'),
283 ElementSelected: Symbol('ElementSelected') 262 ElementSelected: Symbol('ElementSelected')
284 }; 263 };
285 264
286 /** 265 /**
287 * @unrestricted 266 * @unrestricted
288 */ 267 */
289 UI.TreeOutlineInShadow = class extends UI.TreeOutline { 268 UI.TreeOutlineInShadow = class extends UI.TreeOutline {
290 constructor() { 269 /**
291 super(); 270 * @param {boolean=} nonFocusable
271 */
272 constructor(nonFocusable) {
273 super(nonFocusable);
292 this.contentElement.classList.add('tree-outline'); 274 this.contentElement.classList.add('tree-outline');
293 275
294 // Redefine element to the external one. 276 // Redefine element to the external one.
295 this.element = createElement('div'); 277 this.element = createElement('div');
296 this._shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'ui/treeo utline.css'); 278 this._shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'ui/treeo utline.css');
297 this._disclosureElement = this._shadowRoot.createChild('div', 'tree-outline- disclosure'); 279 this._disclosureElement = this._shadowRoot.createChild('div', 'tree-outline- disclosure');
298 this._disclosureElement.appendChild(this.contentElement); 280 this._disclosureElement.appendChild(this.contentElement);
299 this._renderSelection = true; 281 this._renderSelection = true;
300 } 282 }
301 283
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after
675 this._trailingIconsElement = createElementWithClass('div', 'trailing-icons '); 657 this._trailingIconsElement = createElementWithClass('div', 'trailing-icons ');
676 this._trailingIconsElement.classList.add('icons-container'); 658 this._trailingIconsElement.classList.add('icons-container');
677 this._listItemNode.appendChild(this._trailingIconsElement); 659 this._listItemNode.appendChild(this._trailingIconsElement);
678 this._ensureSelection(); 660 this._ensureSelection();
679 } 661 }
680 this._trailingIconsElement.removeChildren(); 662 this._trailingIconsElement.removeChildren();
681 for (var icon of icons) 663 for (var icon of icons)
682 this._trailingIconsElement.appendChild(icon); 664 this._trailingIconsElement.appendChild(icon);
683 } 665 }
684 666
685 /**
686 * @param {boolean} focused
687 */
688 _setFocused(focused) {
689 this._focused = focused;
690 this._listItemNode.classList.toggle('force-white-icons', focused);
691 }
692 667
693 /** 668 /**
694 * @return {string} 669 * @return {string}
695 */ 670 */
696 get tooltip() { 671 get tooltip() {
697 return this._tooltip || ''; 672 return this._tooltip || '';
698 } 673 }
699 674
700 /** 675 /**
701 * @param {string} x 676 * @param {string} x
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after
1021 996
1022 if (this.treeOutline.selectedTreeElement) 997 if (this.treeOutline.selectedTreeElement)
1023 this.treeOutline.selectedTreeElement.deselect(); 998 this.treeOutline.selectedTreeElement.deselect();
1024 this.treeOutline.selectedTreeElement = null; 999 this.treeOutline.selectedTreeElement = null;
1025 1000
1026 if (this.treeOutline._rootElement === this) 1001 if (this.treeOutline._rootElement === this)
1027 return false; 1002 return false;
1028 1003
1029 this.selected = true; 1004 this.selected = true;
1030 1005
1006 this.treeOutline.selectedTreeElement = this;
1007 if (this.treeOutline._focusable)
1008 this._listItemNode.setAttribute('tabIndex', 0);
1031 if (!omitFocus) 1009 if (!omitFocus)
1032 this.treeOutline.focus(); 1010 this.listItemElement.focus();
1033 1011
1034 // Focusing on another node may detach "this" from tree.
1035 if (!this.treeOutline)
1036 return false;
1037 this.treeOutline.selectedTreeElement = this;
1038 this._listItemNode.classList.add('selected'); 1012 this._listItemNode.classList.add('selected');
1039 this._setFocused(this.treeOutline._focused);
1040 this.treeOutline.dispatchEventToListeners(UI.TreeOutline.Events.ElementSelec ted, this); 1013 this.treeOutline.dispatchEventToListeners(UI.TreeOutline.Events.ElementSelec ted, this);
1041 return this.onselect(selectedByUser); 1014 return this.onselect(selectedByUser);
1042 } 1015 }
1043 1016
1044 /** 1017 /**
1045 * @param {boolean=} omitFocus 1018 * @param {boolean=} omitFocus
1046 */ 1019 */
1047 revealAndSelect(omitFocus) { 1020 revealAndSelect(omitFocus) {
1048 this.reveal(true); 1021 this.reveal(true);
1049 this.select(omitFocus); 1022 this.select(omitFocus);
1050 } 1023 }
1051 1024
1052 /** 1025 /**
1053 * @param {boolean=} supressOnDeselect 1026 * @param {boolean=} supressOnDeselect
1054 */ 1027 */
1055 deselect(supressOnDeselect) { 1028 deselect(supressOnDeselect) {
1056 if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !t his.selected) 1029 if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !t his.selected)
pfeldman 2017/01/30 18:43:07 You might want to remove the tabIndex unconditiona
einbinder 2017/02/01 22:51:35 Done.
1057 return; 1030 return;
1058 1031
1059 this.selected = false; 1032 this.selected = false;
1060 this.treeOutline.selectedTreeElement = null; 1033 this.treeOutline.selectedTreeElement = null;
1061 this._listItemNode.classList.remove('selected'); 1034 this._listItemNode.classList.remove('selected');
1062 this._setFocused(false); 1035 this._listItemNode.removeAttribute('tabIndex');
1063 } 1036 }
1064 1037
1065 _populateIfNeeded() { 1038 _populateIfNeeded() {
1066 if (this.treeOutline && this._expandable && !this._children) { 1039 if (this.treeOutline && this._expandable && !this._children) {
1067 this._children = []; 1040 this._children = [];
1068 this.onpopulate(); 1041 this.onpopulate();
1069 } 1042 }
1070 } 1043 }
1071 1044
1072 onpopulate() { 1045 onpopulate() {
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
1204 var paddingLeftValue = window.getComputedStyle(this._listItemNode).paddingLe ft; 1177 var paddingLeftValue = window.getComputedStyle(this._listItemNode).paddingLe ft;
1205 console.assert(paddingLeftValue.endsWith('px')); 1178 console.assert(paddingLeftValue.endsWith('px'));
1206 var computedLeftPadding = parseFloat(paddingLeftValue); 1179 var computedLeftPadding = parseFloat(paddingLeftValue);
1207 var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding; 1180 var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding;
1208 return event.pageX >= left && event.pageX <= left + UI.TreeElement._ArrowTog gleWidth && this._expandable; 1181 return event.pageX >= left && event.pageX <= left + UI.TreeElement._ArrowTog gleWidth && this._expandable;
1209 } 1182 }
1210 }; 1183 };
1211 1184
1212 /** @const */ 1185 /** @const */
1213 UI.TreeElement._ArrowToggleWidth = 10; 1186 UI.TreeElement._ArrowToggleWidth = 10;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698