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

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: 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 19 matching lines...) Expand all
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) {
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
54 /**
55 * @param {boolean} isFocused
56 * @this {UI.TreeOutline}
57 */
58 function setFocused(isFocused) {
59 this._focused = isFocused;
60 if (this.selectedTreeElement)
61 this.selectedTreeElement._setFocused(this._focused);
62 }
63 } 52 }
64 53
65 _createRootElement() { 54 _createRootElement() {
66 this._rootElement = new UI.TreeElement(); 55 this._rootElement = new UI.TreeElement();
67 this._rootElement.treeOutline = this; 56 this._rootElement.treeOutline = this;
68 this._rootElement.root = true; 57 this._rootElement.root = true;
69 this._rootElement.selectable = false; 58 this._rootElement.selectable = false;
70 this._rootElement.expanded = true; 59 this._rootElement.expanded = true;
71 this._rootElement._childrenListNode.classList.remove('children'); 60 this._rootElement._childrenListNode.classList.remove('children');
72 } 61 }
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 return event ? this.treeElementFromPoint(event.pageX, event.pageY) : null; 124 return event ? this.treeElementFromPoint(event.pageX, event.pageY) : null;
136 } 125 }
137 126
138 /** 127 /**
139 * @param {?function(!UI.TreeElement, !UI.TreeElement):number} comparator 128 * @param {?function(!UI.TreeElement, !UI.TreeElement):number} comparator
140 */ 129 */
141 setComparator(comparator) { 130 setComparator(comparator) {
142 this._comparator = comparator; 131 this._comparator = comparator;
143 } 132 }
144 133
145 /** 134 focus() {
146 * @param {boolean} focusable 135 if (this.selectedTreeElement)
147 */ 136 this.selectedTreeElement.listItemElement.focus();
148 setFocusable(focusable) {
149 if (focusable)
150 this.contentElement.setAttribute('tabIndex', 0);
151 else 137 else
152 this.contentElement.removeAttribute('tabIndex'); 138 this.contentElement.focus();
pfeldman 2017/01/21 04:57:34 There is no tabIndex on contentElement, is it focu
einbinder 2017/01/21 06:20:32 Ah, it isn't. I think its ok for empty TreeOutline
153 }
154
155 focus() {
156 this.contentElement.focus();
157 } 139 }
158 140
159 /** 141 /**
160 * @param {!UI.TreeElement} element 142 * @param {!UI.TreeElement} element
161 */ 143 */
162 _bindTreeElement(element) { 144 _bindTreeElement(element) {
163 if (element.treeOutline) 145 if (element.treeOutline)
164 console.error('Binding element for the second time: ' + new Error().stack) ; 146 console.error('Binding element for the second time: ' + new Error().stack) ;
165 element.treeOutline = this; 147 element.treeOutline = this;
166 element.onbind(); 148 element.onbind();
149 if (this._focusable)
150 element.tabIndex = -1;
pfeldman 2017/01/21 04:57:34 element does not have tabIndex property. use setAt
einbinder 2017/01/21 06:20:32 Ok!
167 } 151 }
168 152
169 /** 153 /**
170 * @param {!UI.TreeElement} element 154 * @param {!UI.TreeElement} element
171 */ 155 */
172 _unbindTreeElement(element) { 156 _unbindTreeElement(element) {
173 if (!element.treeOutline) 157 if (!element.treeOutline)
174 console.error('Unbinding element that was not bound: ' + new Error().stack ); 158 console.error('Unbinding element that was not bound: ' + new Error().stack );
175 159
176 element.deselect(); 160 element.deselect();
177 element.onunbind(); 161 element.onunbind();
178 element.treeOutline = null; 162 element.treeOutline = null;
163 element.listItemElement.removeAttribute('tabIndex');
179 } 164 }
180 165
181 /** 166 /**
182 * @return {boolean} 167 * @return {boolean}
183 */ 168 */
184 selectPrevious() { 169 selectPrevious() {
185 var nextSelectedElement = this.selectedTreeElement.traversePreviousTreeEleme nt(true); 170 var nextSelectedElement = this.selectedTreeElement.traversePreviousTreeEleme nt(true);
186 while (nextSelectedElement && !nextSelectedElement.selectable) 171 while (nextSelectedElement && !nextSelectedElement.selectable)
187 nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!thi s.expandTreeElementsWhenArrowing); 172 nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!thi s.expandTreeElementsWhenArrowing);
188 if (nextSelectedElement) { 173 if (nextSelectedElement) {
(...skipping 16 matching lines...) Expand all
205 nextSelectedElement.select(false, true); 190 nextSelectedElement.select(false, true);
206 return true; 191 return true;
207 } 192 }
208 return false; 193 return false;
209 } 194 }
210 195
211 /** 196 /**
212 * @param {!Event} event 197 * @param {!Event} event
213 */ 198 */
214 _treeKeyDown(event) { 199 _treeKeyDown(event) {
215 if (event.target !== this.contentElement) 200 if (!this.selectedTreeElement || event.target !== this.selectedTreeElement.l istItemElement || event.shiftKey ||
pfeldman 2017/01/21 04:57:34 listItemElement can have focusable children, if li
einbinder 2017/01/21 06:20:31 This was specifically to guard against that scenar
216 return; 201 event.metaKey || event.ctrlKey)
217
218 if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ct rlKey)
219 return; 202 return;
220 203
221 var handled = false; 204 var handled = false;
222 if (event.key === 'ArrowUp' && !event.altKey) { 205 if (event.key === 'ArrowUp' && !event.altKey) {
223 handled = this.selectPrevious(); 206 handled = this.selectPrevious();
224 } else if (event.key === 'ArrowDown' && !event.altKey) { 207 } else if (event.key === 'ArrowDown' && !event.altKey) {
225 handled = this.selectNext(); 208 handled = this.selectNext();
226 } else if (event.key === 'ArrowLeft') { 209 } else if (event.key === 'ArrowLeft') {
227 handled = this.selectedTreeElement.collapseOrAscend(event.altKey); 210 handled = this.selectedTreeElement.collapseOrAscend(event.altKey);
228 } else if (event.key === 'ArrowRight') { 211 } else if (event.key === 'ArrowRight') {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 ElementAttached: Symbol('ElementAttached'), 252 ElementAttached: Symbol('ElementAttached'),
270 ElementExpanded: Symbol('ElementExpanded'), 253 ElementExpanded: Symbol('ElementExpanded'),
271 ElementCollapsed: Symbol('ElementCollapsed'), 254 ElementCollapsed: Symbol('ElementCollapsed'),
272 ElementSelected: Symbol('ElementSelected') 255 ElementSelected: Symbol('ElementSelected')
273 }; 256 };
274 257
275 /** 258 /**
276 * @unrestricted 259 * @unrestricted
277 */ 260 */
278 UI.TreeOutlineInShadow = class extends UI.TreeOutline { 261 UI.TreeOutlineInShadow = class extends UI.TreeOutline {
279 constructor() { 262 /**
280 super(); 263 * @param {boolean=} nonFocusable
264 */
265 constructor(nonFocusable) {
266 super(nonFocusable);
281 this.contentElement.classList.add('tree-outline'); 267 this.contentElement.classList.add('tree-outline');
282 268
283 // Redefine element to the external one. 269 // Redefine element to the external one.
284 this.element = createElement('div'); 270 this.element = createElement('div');
285 this._shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'ui/treeo utline.css'); 271 this._shadowRoot = UI.createShadowRootWithCoreStyles(this.element, 'ui/treeo utline.css');
286 this._disclosureElement = this._shadowRoot.createChild('div', 'tree-outline- disclosure'); 272 this._disclosureElement = this._shadowRoot.createChild('div', 'tree-outline- disclosure');
287 this._disclosureElement.appendChild(this.contentElement); 273 this._disclosureElement.appendChild(this.contentElement);
288 this._renderSelection = true; 274 this._renderSelection = true;
289 } 275 }
290 276
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after
664 this._trailingIconsElement = createElementWithClass('div', 'trailing-icons '); 650 this._trailingIconsElement = createElementWithClass('div', 'trailing-icons ');
665 this._trailingIconsElement.classList.add('icons-container'); 651 this._trailingIconsElement.classList.add('icons-container');
666 this._listItemNode.appendChild(this._trailingIconsElement); 652 this._listItemNode.appendChild(this._trailingIconsElement);
667 this._ensureSelection(); 653 this._ensureSelection();
668 } 654 }
669 this._trailingIconsElement.removeChildren(); 655 this._trailingIconsElement.removeChildren();
670 for (var icon of icons) 656 for (var icon of icons)
671 this._trailingIconsElement.appendChild(icon); 657 this._trailingIconsElement.appendChild(icon);
672 } 658 }
673 659
674 /**
675 * @param {boolean} focused
676 */
677 _setFocused(focused) {
678 this._focused = focused;
679 this._listItemNode.classList.toggle('force-white-icons', focused);
680 }
681 660
682 /** 661 /**
683 * @return {string} 662 * @return {string}
684 */ 663 */
685 get tooltip() { 664 get tooltip() {
686 return this._tooltip || ''; 665 return this._tooltip || '';
687 } 666 }
688 667
689 /** 668 /**
690 * @param {string} x 669 * @param {string} x
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
994 973
995 if (this.treeOutline.selectedTreeElement) 974 if (this.treeOutline.selectedTreeElement)
996 this.treeOutline.selectedTreeElement.deselect(); 975 this.treeOutline.selectedTreeElement.deselect();
997 this.treeOutline.selectedTreeElement = null; 976 this.treeOutline.selectedTreeElement = null;
998 977
999 if (this.treeOutline._rootElement === this) 978 if (this.treeOutline._rootElement === this)
1000 return false; 979 return false;
1001 980
1002 this.selected = true; 981 this.selected = true;
1003 982
983 this.treeOutline.selectedTreeElement = this;
984 if (this.treeOutline._focusable)
985 this._listItemNode.tabIndex = 0;
1004 if (!omitFocus) 986 if (!omitFocus)
1005 this.treeOutline.focus(); 987 this.treeOutline.focus();
pfeldman 2017/01/21 04:57:34 Why going into treeOutline just to focus the node
einbinder 2017/01/21 06:20:32 No great reason.
1006 988
1007 // Focusing on another node may detach "this" from tree.
1008 if (!this.treeOutline)
pfeldman 2017/01/21 04:57:34 I'm pretty sure this was happening :(, so removing
einbinder 2017/01/21 06:20:32 I don't see how this can happen, it just calls foc
1009 return false;
1010 this.treeOutline.selectedTreeElement = this;
1011 this._listItemNode.classList.add('selected'); 989 this._listItemNode.classList.add('selected');
1012 this._setFocused(this.treeOutline._focused);
1013 this.treeOutline.dispatchEventToListeners(UI.TreeOutline.Events.ElementSelec ted, this); 990 this.treeOutline.dispatchEventToListeners(UI.TreeOutline.Events.ElementSelec ted, this);
1014 return this.onselect(selectedByUser); 991 return this.onselect(selectedByUser);
1015 } 992 }
1016 993
1017 /** 994 /**
1018 * @param {boolean=} omitFocus 995 * @param {boolean=} omitFocus
1019 */ 996 */
1020 revealAndSelect(omitFocus) { 997 revealAndSelect(omitFocus) {
1021 this.reveal(true); 998 this.reveal(true);
1022 this.select(omitFocus); 999 this.select(omitFocus);
1023 } 1000 }
1024 1001
1025 /** 1002 /**
1026 * @param {boolean=} supressOnDeselect 1003 * @param {boolean=} supressOnDeselect
1027 */ 1004 */
1028 deselect(supressOnDeselect) { 1005 deselect(supressOnDeselect) {
1029 if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !t his.selected) 1006 if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !t his.selected)
1030 return; 1007 return;
1031 1008
1032 this.selected = false; 1009 this.selected = false;
1033 this.treeOutline.selectedTreeElement = null; 1010 this.treeOutline.selectedTreeElement = null;
1034 this._listItemNode.classList.remove('selected'); 1011 this._listItemNode.classList.remove('selected');
1035 this._setFocused(false); 1012 this._listItemNode.tabIndex = -1;
1036 } 1013 }
1037 1014
1038 _populateIfNeeded() { 1015 _populateIfNeeded() {
1039 if (this.treeOutline && this._expandable && !this._children) { 1016 if (this.treeOutline && this._expandable && !this._children) {
1040 this._children = []; 1017 this._children = [];
1041 this.onpopulate(); 1018 this.onpopulate();
1042 } 1019 }
1043 } 1020 }
1044 1021
1045 onpopulate() { 1022 onpopulate() {
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
1177 var paddingLeftValue = window.getComputedStyle(this._listItemNode).paddingLe ft; 1154 var paddingLeftValue = window.getComputedStyle(this._listItemNode).paddingLe ft;
1178 console.assert(paddingLeftValue.endsWith('px')); 1155 console.assert(paddingLeftValue.endsWith('px'));
1179 var computedLeftPadding = parseFloat(paddingLeftValue); 1156 var computedLeftPadding = parseFloat(paddingLeftValue);
1180 var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding; 1157 var left = this._listItemNode.totalOffsetLeft() + computedLeftPadding;
1181 return event.pageX >= left && event.pageX <= left + UI.TreeElement._ArrowTog gleWidth && this._expandable; 1158 return event.pageX >= left && event.pageX <= left + UI.TreeElement._ArrowTog gleWidth && this._expandable;
1182 } 1159 }
1183 }; 1160 };
1184 1161
1185 /** @const */ 1162 /** @const */
1186 UI.TreeElement._ArrowToggleWidth = 10; 1163 UI.TreeElement._ArrowToggleWidth = 10;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698