Index: chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js |
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js |
index 43b60fa2e33a895665bba72defb95631aac1f16f..cee2254a32d29f7e7794f9baa2d2257978ed2866 100644 |
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js |
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js |
@@ -22,7 +22,7 @@ goog.scope(function() { |
var AutomationEvent = chrome.automation.AutomationEvent; |
var AutomationNode = chrome.automation.AutomationNode; |
var Cursor = cursors.Cursor; |
-var Dir = AutomationUtil.Dir; |
+var Dir = constants.Dir; |
var EventType = chrome.automation.EventType; |
var Range = cursors.Range; |
var RoleType = chrome.automation.RoleType; |
@@ -66,8 +66,16 @@ editing.TextEditHandler.prototype = { |
*/ |
function TextFieldTextEditHandler(node) { |
editing.TextEditHandler.call(this, node); |
- /** @type {AutomationEditableText} @private */ |
- this.editableText_ = new AutomationEditableText(node); |
+ |
+ chrome.automation.getDesktop(function(desktop) { |
+ // Chrome channel is empty when building from source. |
+ var useExperimentalRichText = desktop.chromeChannel == '' && |
+ node.state.richlyEditable; |
+ |
+ /** @private {!AutomationEditableText} */ |
+ this.editableText_ = useExperimentalRichText ? |
+ new AutomationRichEditableText(node) : new AutomationEditableText(node); |
+ }.bind(this)); |
} |
TextFieldTextEditHandler.prototype = { |
@@ -199,6 +207,156 @@ AutomationEditableText.prototype = { |
} |
}; |
+ |
+/** |
+ * A |ChromeVoxEditableTextBase| that implements text editing feedback |
+ * for automation tree text fields using anchor and focus selection. |
+ * @constructor |
+ * @param {!AutomationNode} node |
+ * @extends {AutomationEditableText} |
+ */ |
+function AutomationRichEditableText(node) { |
+ AutomationEditableText.call(this, node); |
+ |
+ var root = this.node_.root; |
+ if (!root || !root.anchorObject || !root.focusObject) |
+ return; |
+ |
+ this.range = new cursors.Range( |
+ new cursors.Cursor(root.anchorObject, root.anchorOffset || 0), |
+ new cursors.Cursor(root.focusObject, root.focusOffset || 0)); |
+} |
+ |
+AutomationRichEditableText.prototype = { |
+ __proto__: AutomationEditableText.prototype, |
+ |
+ /** @override */ |
+ onUpdate: function() { |
+ var root = this.node_.root; |
+ if (!root.anchorObject || !root.focusObject) |
+ return; |
+ |
+ var cur = new cursors.Range( |
+ new cursors.Cursor(root.anchorObject, root.anchorOffset || 0), |
+ new cursors.Cursor(root.focusObject, root.focusOffset || 0)); |
+ var prev = this.range; |
+ |
+ if (prev.equals(cur)) |
dmazzoni
2017/05/09 04:49:47
What if the range stays the same but the contents
David Tseng
2017/05/09 23:13:50
Done.
|
+ return; |
+ |
+ this.range = cur; |
+ |
+ if (prev.start.node == cur.start.node && |
+ prev.end.node == cur.end.node && |
+ cur.start.node == cur.end.node) { |
+ // Plain text: diff the two positions. |
+ this.changed(new cvox.TextChangeEvent( |
+ root.anchorObject.name || '', |
+ root.anchorOffset || 0, |
+ root.focusOffset || 0, |
+ true)); |
+ |
+ var lineIndex = this.getLineIndex(this.start); |
+ var brailleLineStart = this.getLineStart(lineIndex); |
+ var buff = new Spannable(this.value); |
+ buff.setSpan(new cvox.ValueSpan(0), 0, this.value.length); |
+ |
+ var selStart = this.start - brailleLineStart; |
+ var selEnd = this.end - brailleLineStart; |
+ buff.setSpan(new cvox.ValueSelectionSpan(), selStart, selEnd); |
+ cvox.ChromeVox.braille.write(new cvox.NavBraille({text: buff, |
+ startIndex: selStart, |
+ endIndex: selEnd})); |
+ return; |
+ } else if (cur.start.equals(prev.start)) { |
dmazzoni
2017/05/09 04:49:47
Do you need a braille output rule for this case?
David Tseng
2017/05/09 23:13:50
Removed.
|
+ // Selection. |
+ var dir = prev.end.compare(cur.end); |
+ var msg = dir == Dir.FORWARD ? '@selected' : '@unselected'; |
+ new Output().withSpeech( |
+ new cursors.Range(cur.end, prev.end), prev, Output.EventType.NAVIGATE) |
+ .formatForSpeech(msg) |
+ .go(); |
+ } else if (cur.end.equals(prev.end)) { |
dmazzoni
2017/05/09 04:49:46
Same, this does this case need braille?
David Tseng
2017/05/09 23:13:50
Removed.
|
+ // Selection. |
+ var dir = prev.start.compare(cur.start); |
+ var msg = dir == Dir.FORWARD ? '@unselected' : '@selected'; |
+ new Output().withSpeech(new cursors.Range( |
+ cur.start, prev.start), prev, Output.EventType.NAVIGATE) |
+ .formatForSpeech(msg) |
+ .go(); |
+ } else { |
+ // Rich text: |
+ // If the position is collapsed, expand to the current line. |
+ var start = cur.start; |
+ var end = cur.end; |
+ if (start.equals(end)) { |
+ start = start.move(Unit.LINE, Movement.BOUND, Dir.BACKWARD); |
+ end = end.move(Unit.LINE, Movement.BOUND, Dir.FORWARD); |
+ } |
+ var range = new cursors.Range(start, end); |
+ var output = new Output().withRichSpeechAndBraille( |
+ range, prev, Output.EventType.NAVIGATE); |
+ |
+ // This position is not describable. |
+ if (!output.hasSpeech) { |
+ cvox.ChromeVox.tts.speak('blank', cvox.QueueMode.CATEGORY_FLUSH); |
+ cvox.ChromeVox.braille.write( |
+ new cvox.NavBraille({text: '', startIndex: 0, endIndex: 0})); |
+ } else { |
+ output.go(); |
+ } |
+ } |
+ |
+ // Keep the other members in sync for any future editable text base state |
+ // machine changes. |
+ this.value = cur.start.node.name || ''; |
+ this.start = cur.start.index; |
+ this.end = cur.start.index; |
+ }, |
+ |
+ /** @override */ |
+ describeSelectionChanged: function(evt) { |
+ // Ignore end of text announcements. |
+ if ((this.start + 1) == evt.start && evt.start == this.value.length) |
+ return; |
+ |
+ cvox.ChromeVoxEditableTextBase.prototype.describeSelectionChanged.call( |
+ this, evt); |
+ }, |
+ |
+ /** @override */ |
+ getLineIndex: function(charIndex) { |
+ var breaks = this.getLineBreaks_(); |
+ var index = 0; |
+ while (index < breaks.length && breaks[index] <= charIndex) |
+ ++index; |
+ return index; |
+ }, |
+ |
+ /** @override */ |
+ getLineStart: function(lineIndex) { |
+ if (lineIndex == 0) |
+ return 0; |
+ var breaks = this.getLineBreaks_(); |
+ return breaks[lineIndex - 1] || |
+ this.node_.root.focusObject.value.length; |
+ }, |
+ |
+ /** @override */ |
+ getLineEnd: function(lineIndex) { |
+ var breaks = this.getLineBreaks_(); |
+ var value = this.node_.root.focusObject.name; |
+ if (lineIndex >= breaks.length) |
+ return value.length; |
+ return breaks[lineIndex]; |
+ }, |
+ |
+ /** @override */ |
+ getLineBreaks_: function() { |
+ return this.node_.root.focusObject.lineStartOffsets || []; |
+ } |
+}; |
+ |
/** |
* @param {!AutomationNode} node The root editable node, i.e. the root of a |
* contenteditable subtree or a text field. |