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..4d40067b035e09515e8516578ac052ec11485667 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; |
@@ -41,6 +41,12 @@ editing.TextEditHandler = function(node) { |
this.node_ = node; |
}; |
+/** |
+ * Flag set to indicate whether ChromeVox uses experimental rich text support. |
+ * @type {boolean} |
+ */ |
+editing.useRichText = false; |
+ |
editing.TextEditHandler.prototype = { |
/** @return {!AutomationNode} */ |
get node() { |
@@ -66,8 +72,15 @@ editing.TextEditHandler.prototype = { |
*/ |
function TextFieldTextEditHandler(node) { |
editing.TextEditHandler.call(this, node); |
- /** @type {AutomationEditableText} @private */ |
- this.editableText_ = new AutomationEditableText(node); |
+ |
+ chrome.automation.getDesktop(function(desktop) { |
+ var useRichText = editing.useRichText && |
+ node.state.richlyEditable; |
+ |
+ /** @private {!AutomationEditableText} */ |
+ this.editableText_ = useRichText ? |
+ new AutomationRichEditableText(node) : new AutomationEditableText(node); |
+ }.bind(this)); |
} |
TextFieldTextEditHandler.prototype = { |
@@ -199,6 +212,138 @@ 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; |
+ |
+ 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 brailleLineEnd = this.getLineEnd(lineIndex); |
+ var buff = new Spannable(this.value); |
+ buff.setSpan(new cvox.ValueSpan(0), brailleLineStart, brailleLineEnd); |
+ |
+ 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 { |
+ // 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. |