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

Side by Side Diff: chrome/browser/resources/options/inline_editable_list.js

Issue 688043003: Maintain focused column in chrome://settings/searchEngines (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@split1_05
Patch Set: Rebase Created 6 years, 1 month 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
« no previous file with comments | « no previous file | chrome/browser/resources/options/search_engine_manager_engine_list.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 cr.define('options', function() { 5 cr.define('options', function() {
6 /** @const */ var DeletableItem = options.DeletableItem; 6 /** @const */ var DeletableItem = options.DeletableItem;
7 /** @const */ var DeletableItemList = options.DeletableItemList; 7 /** @const */ var DeletableItemList = options.DeletableItemList;
8 8
9 /** 9 /**
10 * Creates a new list item with support for inline editing. 10 * Creates a new list item with support for inline editing.
(...skipping 13 matching lines...) Expand all
24 */ 24 */
25 InlineEditableItem.decorate = function(el) { 25 InlineEditableItem.decorate = function(el) {
26 el.__proto__ = InlineEditableItem.prototype; 26 el.__proto__ = InlineEditableItem.prototype;
27 el.decorate(); 27 el.decorate();
28 }; 28 };
29 29
30 InlineEditableItem.prototype = { 30 InlineEditableItem.prototype = {
31 __proto__: DeletableItem.prototype, 31 __proto__: DeletableItem.prototype,
32 32
33 /** 33 /**
34 * Index of currently focused column, or -1 for none.
35 * @type {number}
36 */
37 focusedColumnIndex: -1,
38
39 /**
34 * Whether or not this item can be edited. 40 * Whether or not this item can be edited.
35 * @type {boolean} 41 * @type {boolean}
36 * @private 42 * @private
37 */ 43 */
38 editable_: true, 44 editable_: true,
39 45
40 /** 46 /**
41 * Whether or not this is a placeholder for adding a new item. 47 * Whether or not this is a placeholder for adding a new item.
42 * @type {boolean} 48 * @type {boolean}
43 * @private 49 * @private
(...skipping 23 matching lines...) Expand all
67 */ 73 */
68 editClickTarget_: null, 74 editClickTarget_: null,
69 75
70 /** @override */ 76 /** @override */
71 decorate: function() { 77 decorate: function() {
72 DeletableItem.prototype.decorate.call(this); 78 DeletableItem.prototype.decorate.call(this);
73 79
74 this.editFields_ = []; 80 this.editFields_ = [];
75 this.addEventListener('mousedown', this.handleMouseDown_); 81 this.addEventListener('mousedown', this.handleMouseDown_);
76 this.addEventListener('keydown', this.handleKeyDown_); 82 this.addEventListener('keydown', this.handleKeyDown_);
77 this.addEventListener('leadChange', this.handleLeadChange_); 83 this.addEventListener('focusin', this.handleFocusIn_);
78 }, 84 },
79 85
80 /** @override */ 86 /** @override */
81 selectionChanged: function() { 87 selectionChanged: function() {
82 this.updateEditState(); 88 this.updateEditState();
83 }, 89 },
84 90
85 /** 91 /**
86 * Called when this element gains or loses 'lead' status. Updates editing 92 * Called when this element gains or loses 'lead' status. Updates editing
87 * mode accordingly. 93 * mode accordingly.
88 * @private
89 */ 94 */
90 handleLeadChange_: function() { 95 updateLeadState: function() {
91 // Add focusability before call to updateEditState. 96 // Add focusability before call to updateEditState.
92 if (this.lead) { 97 if (this.lead) {
93 this.setEditableValuesFocusable(true); 98 this.setEditableValuesFocusable(true);
94 this.setCloseButtonFocusable(true); 99 this.setCloseButtonFocusable(true);
95 } 100 }
96 101
97 this.updateEditState(); 102 this.updateEditState();
98 103
99 // Remove focusability after call to updateEditState. 104 // Remove focusability after call to updateEditState.
100 this.setStaticValuesFocusable(false); 105 this.setStaticValuesFocusable(false);
(...skipping 25 matching lines...) Expand all
126 if (editing) 131 if (editing)
127 this.setAttribute('editing', ''); 132 this.setAttribute('editing', '');
128 else 133 else
129 this.removeAttribute('editing'); 134 this.removeAttribute('editing');
130 135
131 if (editing) { 136 if (editing) {
132 this.editCancelled_ = false; 137 this.editCancelled_ = false;
133 138
134 cr.dispatchSimpleEvent(this, 'edit', true); 139 cr.dispatchSimpleEvent(this, 'edit', true);
135 140
136 var focusElement = this.editClickTarget_ || this.initialFocusElement; 141 var focusElement = this.getEditFocusElement_();
137 this.editClickTarget_ = null;
138 if (focusElement) 142 if (focusElement)
139 this.focusAndMaybeSelect_(focusElement); 143 this.focusAndMaybeSelect_(focusElement);
140 } else { 144 } else {
141 if (!this.editCancelled_ && this.hasBeenEdited && 145 if (!this.editCancelled_ && this.hasBeenEdited &&
142 this.currentInputIsValid) { 146 this.currentInputIsValid) {
143 if (this.isPlaceholder) 147 if (this.isPlaceholder)
144 this.parentNode.focusPlaceholder = true; 148 this.parentNode.focusPlaceholder = true;
145 149
146 this.updateStaticValues_(); 150 this.updateStaticValues_();
147 cr.dispatchSimpleEvent(this, 'commitedit', true); 151 cr.dispatchSimpleEvent(this, 'commitedit', true);
148 } else { 152 } else {
149 this.resetEditableValues_(); 153 this.resetEditableValues_();
150 cr.dispatchSimpleEvent(this, 'canceledit', true); 154 cr.dispatchSimpleEvent(this, 'canceledit', true);
151 } 155 }
152 } 156 }
153 }, 157 },
154 158
155 /** 159 /**
160 * Return editable element that should be focused, or null for none.
161 * @private
162 */
163 getEditFocusElement_: function() {
164 // If an edit field was clicked on then use the clicked element.
165 if (this.editClickTarget_) {
166 var result = this.editClickTarget_;
167 this.editClickTarget_ = null;
168 return result;
169 }
170
171 // If focusedColumnIndex is valid then use the element in that column.
172 if (this.focusedColumnIndex != -1) {
173 var nearestColumn =
174 this.getNearestColumnByIndex_(this.focusedColumnIndex);
175 if (nearestColumn)
176 return nearestColumn;
177 }
178
179 // It's possible that focusedColumnIndex hasn't been updated yet.
180 // Check getFocusedColumnIndex_ directly.
181 // This can't completely replace the above focusedColumnIndex check
182 // because InlineEditableItemList may have set focusedColumnIndex to a
183 // different value.
184 var columnIndex = this.getFocusedColumnIndex_();
185 if (columnIndex != -1) {
186 var nearestColumn = this.getNearestColumnByIndex_(columnIndex);
187 if (nearestColumn)
188 return nearestColumn;
189 }
190
191 // Everything else failed so return the default.
192 return this.initialFocusElement;
193 },
194
195 /**
156 * Focus on the specified element, and select the editable text in it 196 * Focus on the specified element, and select the editable text in it
157 * if possible. 197 * if possible.
158 * @param {!Element} control An element to be focused. 198 * @param {!Element} control An element to be focused.
159 * @private 199 * @private
160 */ 200 */
161 focusAndMaybeSelect_: function(control) { 201 focusAndMaybeSelect_: function(control) {
162 control.focus(); 202 control.focus();
163 if (control.tagName == 'INPUT') 203 if (control.tagName == 'INPUT')
164 control.select(); 204 control.select();
165 }, 205 },
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after
333 if (this.editable) 373 if (this.editable)
334 control.staticVersion.tabIndex = -1; 374 control.staticVersion.tabIndex = -1;
335 control.staticVersion.editableVersion = control; 375 control.staticVersion.editableVersion = control;
336 control.staticVersion.addEventListener('focus', 376 control.staticVersion.addEventListener('focus',
337 this.handleFocus.bind(this)); 377 this.handleFocus.bind(this));
338 } 378 }
339 this.editFields_.push(control); 379 this.editFields_.push(control);
340 }, 380 },
341 381
342 /** 382 /**
383 * Set the column index for a child element of this InlineEditableItem.
384 * Only elements with a column index will be keyboard focusable, e.g. by
385 * pressing the tab key.
386 * @param {Element} element Element whose column index to set. Method does
387 * nothing if element is null.
388 * @param {number} columnIndex The new column index to set on the element.
389 * -1 removes the column index.
390 */
391 setFocusableColumnIndex: function(element, columnIndex) {
392 if (!element)
393 return;
394
395 if (columnIndex >= 0)
396 element.setAttribute('inlineeditable-column', columnIndex);
397 else
398 element.removeAttribute('inlineeditable-column');
399 },
400
401 /**
343 * Resets the editable version of any controls created by createEditable* 402 * Resets the editable version of any controls created by createEditable*
344 * to match the static text. 403 * to match the static text.
345 * @private 404 * @private
346 */ 405 */
347 resetEditableValues_: function() { 406 resetEditableValues_: function() {
348 var editFields = this.editFields_; 407 var editFields = this.editFields_;
349 for (var i = 0; i < editFields.length; i++) { 408 for (var i = 0; i < editFields.length; i++) {
350 var staticLabel = editFields[i].staticVersion; 409 var staticLabel = editFields[i].staticVersion;
351 if (!staticLabel && !this.isPlaceholder) 410 if (!staticLabel && !this.isPlaceholder)
352 continue; 411 continue;
(...skipping 21 matching lines...) Expand all
374 if (!staticLabel) 433 if (!staticLabel)
375 continue; 434 continue;
376 435
377 if (editFields[i].tagName == 'INPUT') 436 if (editFields[i].tagName == 'INPUT')
378 staticLabel.textContent = editFields[i].value; 437 staticLabel.textContent = editFields[i].value;
379 // Add more tag types here as new createEditable* methods are added. 438 // Add more tag types here as new createEditable* methods are added.
380 } 439 }
381 }, 440 },
382 441
383 /** 442 /**
443 * Returns the index of the column that currently has focus, or -1 if no
444 * column has focus.
445 * @return {number}
446 * @private
447 */
448 getFocusedColumnIndex_: function() {
449 var element = document.activeElement.editableVersion ||
450 document.activeElement;
451
452 if (element.hasAttribute('inlineeditable-column'))
453 return parseInt(element.getAttribute('inlineeditable-column'), 10);
454 return -1;
455 },
456
457 /**
458 * Returns the element from the column that has the largest index where:
459 * where:
460 * + index <= startIndex, and
461 * + the element exists, and
462 * + the element is not disabled
463 * @return {Element}
464 * @private
465 */
466 getNearestColumnByIndex_: function(startIndex) {
467 for (var i = startIndex; i >= 0; --i) {
468 var el = this.querySelector('[inlineeditable-column="' + i + '"]');
469 if (el && !el.disabled)
470 return el;
471 }
472 return null;
473 },
474
475 /**
384 * Called when a key is pressed. Handles committing and canceling edits. 476 * Called when a key is pressed. Handles committing and canceling edits.
385 * @param {Event} e The key down event. 477 * @param {Event} e The key down event.
386 * @private 478 * @private
387 */ 479 */
388 handleKeyDown_: function(e) { 480 handleKeyDown_: function(e) {
389 if (!this.editing) 481 if (!this.editing)
390 return; 482 return;
391 483
392 var endEdit = false; 484 var endEdit = false;
393 var handledKey = true; 485 var handledKey = true;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 // from currently selected edit field. 533 // from currently selected edit field.
442 e.stopPropagation(); 534 e.stopPropagation();
443 e.preventDefault(); 535 e.preventDefault();
444 } 536 }
445 return; 537 return;
446 } 538 }
447 539
448 if (editClickTarget && !editClickTarget.disabled) 540 if (editClickTarget && !editClickTarget.disabled)
449 this.editClickTarget_ = editClickTarget; 541 this.editClickTarget_ = editClickTarget;
450 }, 542 },
543
544 /**
545 * Called when this InlineEditableItem or any of its children are given
546 * focus. Updates focusedColumnIndex with the index of the newly focused
547 * column, or -1 if the focused element does not have a column index.
548 * @param {Event} e The focusin event.
549 * @private
550 */
551 handleFocusIn_: function(e) {
552 var target = e.target.editableVersion || e.target;
553 this.focusedColumnIndex = target.hasAttribute('inlineeditable-column') ?
554 parseInt(target.getAttribute('inlineeditable-column'), 10) : -1;
555 },
451 }; 556 };
452 557
453 /** 558 /**
454 * Takes care of committing changes to inline editable list items when the 559 * Takes care of committing changes to inline editable list items when the
455 * window loses focus. 560 * window loses focus.
456 */ 561 */
457 function handleWindowBlurs() { 562 function handleWindowBlurs() {
458 window.addEventListener('blur', function(e) { 563 window.addEventListener('blur', function(e) {
459 var itemAncestor = findAncestor(document.activeElement, function(node) { 564 var itemAncestor = findAncestor(document.activeElement, function(node) {
460 return node instanceof InlineEditableItem; 565 return node instanceof InlineEditableItem;
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
497 * @param {Event} e The change event. 602 * @param {Event} e The change event.
498 * @private 603 * @private
499 */ 604 */
500 handleListFocusChange_: function(e) { 605 handleListFocusChange_: function(e) {
501 var leadItem = this.getListItemByIndex(this.selectionModel.leadIndex); 606 var leadItem = this.getListItemByIndex(this.selectionModel.leadIndex);
502 if (leadItem) { 607 if (leadItem) {
503 if (e.newValue) { 608 if (e.newValue) {
504 // Add focusability before making other changes. 609 // Add focusability before making other changes.
505 leadItem.setEditableValuesFocusable(true); 610 leadItem.setEditableValuesFocusable(true);
506 leadItem.setCloseButtonFocusable(true); 611 leadItem.setCloseButtonFocusable(true);
612 leadItem.focusedColumnIndex = -1;
507 leadItem.updateEditState(); 613 leadItem.updateEditState();
508 // Remove focusability after making other changes. 614 // Remove focusability after making other changes.
509 leadItem.setStaticValuesFocusable(false); 615 leadItem.setStaticValuesFocusable(false);
510 } else { 616 } else {
511 // Add focusability before making other changes. 617 // Add focusability before making other changes.
512 leadItem.setStaticValuesFocusable(true); 618 leadItem.setStaticValuesFocusable(true);
513 leadItem.setCloseButtonFocusable(true); 619 leadItem.setCloseButtonFocusable(true);
514 leadItem.editing = false; 620 leadItem.editing = false;
515 // Remove focusability after making other changes. 621 // Remove focusability after making other changes.
516 if (!leadItem.isPlaceholder) 622 if (!leadItem.isPlaceholder)
517 leadItem.setEditableValuesFocusable(false); 623 leadItem.setEditableValuesFocusable(false);
518 } 624 }
519 } 625 }
520 }, 626 },
521 627
522 /** 628 /** @override */
523 * Called after the DataModel for the list has been set. 629 handleLeadChange: function(e) {
524 * @override 630 DeletableItemList.prototype.handleLeadChange.call(this, e);
525 */ 631
632 var focusedColumnIndex = -1;
633 if (e.oldValue != -1) {
634 var element = this.getListItemByIndex(e.oldValue);
635 if (element) {
636 focusedColumnIndex = element.focusedColumnIndex;
637 element.updateLeadState();
638 }
639 }
640
641 if (e.newValue != -1) {
642 var element = this.getListItemByIndex(e.newValue);
643 if (element) {
644 element.focusedColumnIndex = focusedColumnIndex;
645 element.updateLeadState();
646 }
647 }
648 },
649
650 /** @override */
526 onSetDataModelComplete: function() { 651 onSetDataModelComplete: function() {
527 DeletableItemList.prototype.onSetDataModelComplete.call(this); 652 DeletableItemList.prototype.onSetDataModelComplete.call(this);
528 653
529 var firstItem = this.getListItemByIndex(0); 654 var firstItem = this.getListItemByIndex(0);
530 if (firstItem) { 655 if (firstItem) {
531 firstItem.setStaticValuesFocusable(true); 656 firstItem.setStaticValuesFocusable(true);
532 firstItem.setCloseButtonFocusable(true); 657 firstItem.setCloseButtonFocusable(true);
533 if (firstItem.isPlaceholder) 658 if (firstItem.isPlaceholder)
534 firstItem.setEditableValuesFocusable(true); 659 firstItem.setEditableValuesFocusable(true);
535 } 660 }
536 }, 661 },
537 662
538 /** 663 /**
539 * May be overridden by subclasses to disable focusing the placeholder. 664 * May be overridden by subclasses to disable focusing the placeholder.
540 * @return {boolean} True if the placeholder element should be focused on 665 * @return {boolean} True if the placeholder element should be focused on
541 * edit commit. 666 * edit commit.
542 */ 667 */
543 shouldFocusPlaceholder: function() { 668 shouldFocusPlaceholder: function() {
544 return true; 669 return true;
545 }, 670 },
546 }; 671 };
547 672
548 // Export 673 // Export
549 return { 674 return {
550 InlineEditableItem: InlineEditableItem, 675 InlineEditableItem: InlineEditableItem,
551 InlineEditableItemList: InlineEditableItemList, 676 InlineEditableItemList: InlineEditableItemList,
552 }; 677 };
553 }); 678 });
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/resources/options/search_engine_manager_engine_list.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698