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

Unified Diff: chrome/browser/resources/options/inline_editable_list.js

Issue 664583006: Improve keyboard navigation on 'Manage search engines' options page (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/options/inline_editable_list.js
diff --git a/chrome/browser/resources/options/inline_editable_list.js b/chrome/browser/resources/options/inline_editable_list.js
index 89d10e7309592acb2871034315782d7fcaef7e6f..2ef2b7878c20de74929912d6946043a72cec311e 100644
--- a/chrome/browser/resources/options/inline_editable_list.js
+++ b/chrome/browser/resources/options/inline_editable_list.js
@@ -67,14 +67,29 @@ cr.define('options', function() {
*/
editClickTarget_: null,
+ /**
+ * Index of currently focused column, or -1 for none.
+ * @type {number}
+ * @private
+ */
+ focusedColumnIndex_: -1,
+
+ /**
+ * Whether or not to ignore the next mouseup event.
+ * @type {boolean}
+ * @private
+ */
+ ignoreMouseUp_: false,
Dan Beam 2014/10/18 01:35:44 from this class' point of view, isn't this actuall
+
/** @override */
decorate: function() {
DeletableItem.prototype.decorate.call(this);
this.editFields_ = [];
this.addEventListener('mousedown', this.handleMouseDown_);
+ this.addEventListener('mouseup', this.handleMouseUp_);
Dan Beam 2014/10/18 01:35:43 why is this necessary? what are we preventing the
this.addEventListener('keydown', this.handleKeyDown_);
- this.addEventListener('leadChange', this.handleLeadChange_);
+ this.addEventListener('focusin', this.handleFocusIn_);
},
/** @override */
@@ -83,12 +98,24 @@ cr.define('options', function() {
},
/**
- * Called when this element gains or loses 'lead' status. Updates editing
- * mode accordingly.
- * @private
+ * Whether the fields of this InlineEditableItem can be focused using the
+ * keyboard, e.g. by pressing the tab key.
+ * @type {boolean}
*/
- handleLeadChange_: function() {
- this.updateEditState();
+ set focusable(focusable) {
bondd 2014/10/17 01:23:15 Add a getter for completeness, even though we neve
Dan Beam 2014/10/18 01:35:43 remove until needed
+ this.setEditableValuesFocusable_(focusable);
+ this.closeButtonFocusable = focusable;
+ },
+
+ /**
+ * Index of currently focused column, or -1 for none.
+ * @type {number}
+ */
+ get focusedColumnIndex() {
+ return this.focusedColumnIndex_;
Dan Beam 2014/10/18 01:35:44 wrong indent
+ },
+ set focusedColumnIndex(focusedColumnIndex) {
+ this.focusedColumnIndex_ = focusedColumnIndex;
Dan Beam 2014/10/18 01:35:44 how is this useful over just a normal property? e
},
/**
@@ -120,8 +147,20 @@ cr.define('options', function() {
cr.dispatchSimpleEvent(this, 'edit', true);
Dan Beam 2014/10/18 01:35:44 can you add a comment to what all of this is doing
bondd 2014/10/29 21:12:39 Done.
- var focusElement = this.editClickTarget_ || this.initialFocusElement;
- this.editClickTarget_ = null;
+ var focusElement = this.initialFocusElement;
+ if (this.editClickTarget_) {
+ focusElement = this.editClickTarget_;
+ this.editClickTarget_ = null;
Dan Beam 2014/10/18 01:35:44 so why is this code better? is it subtly differen
+ }
Dan Beam 2014/10/18 01:35:44 } else if (...) { (else on same line)
+ else if (this.focusedColumnIndex_ != -1) {
+ focusElement =
+ this.getNearestColumnByIndex_(this.focusedColumnIndex_);
+ }
+ else {
+ var focusedColumn = this.getFocusedColumn_();
+ if (focusedColumn)
+ focusElement = focusedColumn;
+ }
if (focusElement) {
var self = this;
@@ -239,17 +278,11 @@ cr.define('options', function() {
var container = /** @type {HTMLElement} */(
this.ownerDocument.createElement('div'));
var textEl = null;
- if (!this.isPlaceholder) {
- textEl = this.ownerDocument.createElement('div');
- textEl.className = 'static-text';
- textEl.textContent = text;
- textEl.setAttribute('displaymode', 'static');
- container.appendChild(textEl);
- }
Dan Beam 2014/10/18 01:35:44 did you mean to remove this? because you left |te
var inputEl = this.ownerDocument.createElement('input');
inputEl.type = 'text';
inputEl.value = text;
+ inputEl.tabIndex = -1;
if (!this.isPlaceholder) {
inputEl.setAttribute('displaymode', 'edit');
} else {
@@ -294,6 +327,24 @@ cr.define('options', function() {
},
/**
+ * Set the column index for a child element of this InlineEditableItem.
+ * Only elements with a column index will be keyboard focusable, e.g. by
+ * pressing the tab key.
+ * @param {Element} element Element whose column index to set. Method does
+ * nothing if element is null.
+ * @param {number} columnIndex The new column index to set on the element.
+ * -1 removes the column index.
+ */
+ setFocusableColumnIndex: function(element, columnIndex) {
+ if(element) {
Dan Beam 2014/10/18 01:35:44 if (!element) return; ...
bondd 2014/10/29 21:12:39 Done.
+ if (columnIndex >= 0)
+ element.setAttribute('inlineeditable-column', columnIndex);
+ else
+ element.removeAttribute('inlineeditable-column');
+ }
+ },
+
+ /**
* Resets the editable version of any controls created by createEditable*
* to match the static text.
* @private
@@ -335,6 +386,60 @@ cr.define('options', function() {
},
/**
+ * Called when lead status is gained/lost.
+ * @private
+ */
+ updateLeadState_: function() {
+ this.focusable = this.lead;
+ this.updateEditState();
+ },
+
+ /**
+ * Sets whether the editable values can be given focus using the keyboard,
+ * e.g. by pressing the tab key.
+ * @param {boolean}
+ * @private
+ */
+ setEditableValuesFocusable_: function(focusable) {
+ var newTabIndex = focusable ? 0 : -1;
+ var editFields = this.editFields_;
+ for (var i = 0; i < editFields.length; i++)
+ editFields[i].tabIndex = newTabIndex;
Dan Beam 2014/10/18 01:35:44 nit: curlies around 1-line for loops (no curlies a
+ },
+
+ /**
+ * Returns the element in the column that currently has focus, or null
+ * if no column has focus.
+ * @return {Element}
+ * @private
+ */
+ getFocusedColumn_: function() {
+ if (document.activeElement &&
Dan Beam 2014/10/18 01:35:43 there will always be an activeElement, despite wha
+ document.activeElement.hasAttribute('inlineeditable-column')) {
+ return document.activeElement;
+ }
+ return null;
+ },
+
+ /**
+ * Returns the element from the column that has the largest index where:
+ * where:
+ * + index <= startIndex, and
+ * + the element exists, and
+ * + the element is not disabled
+ * @return {Element}
+ * @private
+ */
+ getNearestColumnByIndex_: function(startIndex) {
+ for (var i = startIndex; i >= 0; --i) {
+ var el = this.querySelector('[inlineeditable-column="' + i + '"]');
+ if (el && !el.disabled)
Dan Beam 2014/10/18 01:35:44 nit: maybe add !el.hidden here unless you know tha
bondd 2014/10/29 21:12:39 Under the new split CL it's okay for the element t
+ return el;
+ }
+ return null;
+ },
+
+ /**
* Called when a key is pressed. Handles committing and canceling edits.
* @param {Event} e The key down event.
* @private
@@ -375,7 +480,7 @@ cr.define('options', function() {
* @private
*/
handleMouseDown_: function(e) {
- if (!this.editable || this.editing)
+ if (!this.editable || this.editing || !e.target)
Dan Beam 2014/10/18 01:35:44 when can !e.target ever happen? is there some tes
bondd 2014/10/29 21:12:39 Done.
return;
var clickTarget = e.target;
@@ -386,10 +491,40 @@ cr.define('options', function() {
if (editFields[i] == clickTarget ||
editFields[i].staticVersion == clickTarget) {
this.editClickTarget_ = editFields[i];
+ // Prevent mouse up event from deselecting text.
+ this.ignoreMouseUp_ = true;
return;
}
}
},
+
+ /**
+ * Called when mouse up happens on the list item or any of its children.
+ * @param {Event} e The mouse up event.
+ * @private
+ */
+ handleMouseUp_: function(e) {
+ if (this.ignoreMouseUp_) {
Dan Beam 2014/10/18 01:35:44 so what happens if you mouse down inside the item
+ this.ignoreMouseUp_ = false;
+ e.preventDefault();
+ }
+ },
+
+ /**
+ * Called when this InlineEditableItem or any of its children are given
+ * focus. Updates focusedColumnIndex_ with the index of the newly focused
+ * column, or -1 if the focused element does not have a column index.
+ * @param {Event} e The focusin event.
+ * @private
+ */
+ handleFocusIn_: function(e) {
+ if(e.target.hasAttribute('inlineeditable-column')) {
Dan Beam 2014/10/18 01:35:44 if( => if (
bondd 2014/10/29 21:12:39 Done.
+ this.focusedColumnIndex_ =
+ e.target.getAttribute('inlineeditable-column');
+ }
+ else
Dan Beam 2014/10/18 01:35:43 } else { // even though this part is a 1-liner,
+ this.focusColumnIndex_ = -1;
bondd 2014/10/28 22:31:27 focusedColumnIndex_
bondd 2014/10/29 21:12:39 Done.
+ },
};
/**
@@ -428,6 +563,7 @@ cr.define('options', function() {
this.setAttribute('inlineeditable', '');
this.addEventListener('hasElementFocusChange',
this.handleListFocusChange_);
+ this.removeAttribute('tabIndex');
Dan Beam 2014/10/18 01:35:43 tabindex
},
/**
@@ -436,17 +572,55 @@ cr.define('options', function() {
* @param {Event} e The change event.
* @private
*/
- handleListFocusChange_: function(e) {
+ handleListFocusChange_: function(e) {
var leadItem = this.getListItemByIndex(this.selectionModel.leadIndex);
if (leadItem) {
- if (e.newValue)
+ if (e.newValue) {
+ leadItem.focusedColumnIndex = -1;
leadItem.updateEditState();
+ }
else
leadItem.editing = false;
}
},
/**
+ * Handles a change of the lead item from the selection model.
+ * @param {Event} pe The property change event.
Dan Beam 2014/10/18 01:35:44 pe => e, event, pce (in order of preference)
bondd 2014/10/29 21:12:39 Done.
+ * @override
+ */
+ handleLeadChange: function(pe) {
+ DeletableItemList.prototype.handleLeadChange.call(this, pe);
+
+ var focusedColumnIndex = -1;
+ var element;
+ if (pe.oldValue != -1) {
+ if ((element = this.getListItemByIndex(pe.oldValue))) {
Dan Beam 2014/10/18 01:35:44 don't do assignment in conditionals
bondd 2014/10/29 21:12:39 Done.
+ focusedColumnIndex = element.focusedColumnIndex;
+ element.updateLeadState_();
bondd 2014/10/28 22:31:27 updateLeadState_ is private to a different class
+ }
+ }
+
+ if (pe.newValue != -1) {
+ if ((element = this.getListItemByIndex(pe.newValue))) {
+ element.focusedColumnIndex = focusedColumnIndex;
+ element.updateLeadState_();
bondd 2014/10/28 22:31:27 updateLeadState_ is private to a different class
+ }
+ }
+ },
+
+ /**
+ * Called after the DataModel for the list has been set.
Dan Beam 2014/10/18 01:35:43 * @override
bondd 2014/10/28 22:31:27 Done.
+ */
+ onSetDataModelComplete: function() {
+ DeletableItemList.prototype.onSetDataModelComplete.call(this);
+
+ var firstItem = this.getListItemByIndex(0);
+ if (firstItem)
+ firstItem.focusable = true;
+ },
+
+ /**
* May be overridden by subclasses to disable focusing the placeholder.
* @return {boolean} True if the placeholder element should be focused on
* edit commit.

Powered by Google App Engine
This is Rietveld 408576698