Commit 031ae100 authored by David Tseng's avatar David Tseng Committed by Commit Bot

Add MultiSpannable class to support discontinuous braille spans

Bug: 
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: Ib130dbf92ed60c750e454abb32bf7dcb5c898c92
Reviewed-on: https://chromium-review.googlesource.com/619619Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Commit-Queue: David Tseng <dtseng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#496554}
parent f418c0fb
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* @fileoverview Class which allows construction of annotated strings. * @fileoverview Class which allows construction of annotated strings.
*/ */
goog.provide('MultiSpannable');
goog.provide('Spannable'); goog.provide('Spannable');
goog.scope(function() { goog.scope(function() {
...@@ -60,6 +61,16 @@ Spannable.prototype = { ...@@ -60,6 +61,16 @@ Spannable.prototype = {
*/ */
setSpan: function(value, start, end) { setSpan: function(value, start, end) {
this.removeSpan(value); this.removeSpan(value);
this.setSpanInternal(value, start, end);
},
/**
* @param {*} value Annotation.
* @param {number} start Starting index (inclusive).
* @param {number} end Ending index (exclusive).
* @protected
*/
setSpanInternal: function(value, start, end) {
if (0 <= start && start <= end && end <= this.string_.length) { if (0 <= start && start <= end && end <= this.string_.length) {
// Zero-length spans are explicitly allowed, because it is possible to // Zero-length spans are explicitly allowed, because it is possible to
// query for position by annotation as well as the reverse. // query for position by annotation as well as the reverse.
...@@ -174,6 +185,15 @@ Spannable.prototype = { ...@@ -174,6 +185,15 @@ Spannable.prototype = {
return this.getSpanByValueOrThrow_(value).end; return this.getSpanByValueOrThrow_(value).end;
}, },
/**
* @param {*} value Annotation.
* @return {!Array<{start: number, end: number}>}
*/
getSpanIntervals: function(value) {
return this.spans_.filter(function(s) { return s.value == value; })
.map(function(s) { return {start: s.start, end: s.end}; });
},
/** /**
* Returns the number of characters covered by the given span. Throws if * Returns the number of characters covered by the given span. Throws if
* the span is not in this object. * the span is not in this object.
...@@ -308,6 +328,36 @@ Spannable.prototype = { ...@@ -308,6 +328,36 @@ Spannable.prototype = {
} }
}; };
/**
* A spannable that allows a span value to annotate discontinuous regions of the
* string. In effect, a span value can be set multiple times.
* Note that most methods that assume a span value is unique such as
* |getSpanStart| will use the first span value.
* @constructor
* @param {string|!Spannable=} opt_string Initial value of the spannable.
* @param {*=} opt_annotation Initial annotation for the entire string.
* @extends {Spannable}
*/
MultiSpannable = function(opt_string, opt_annotation) {
Spannable.call(this, opt_string, opt_annotation);
};
MultiSpannable.prototype = {
__proto__: Spannable.prototype,
/** @override */
setSpan: function(value, start, end) {
this.setSpanInternal(value, start, end);
},
/** @override */
substring: function(start, opt_end) {
var ret = Spannable.prototype.substring.call(this, start, opt_end);
return new MultiSpannable(ret);
},
};
/** /**
* Creates a spannable from a json serializable representation. * Creates a spannable from a json serializable representation.
* @param {!SerializedSpannable} obj object containing the serializable * @param {!SerializedSpannable} obj object containing the serializable
......
...@@ -506,3 +506,31 @@ TEST_F('CvoxSpannableUnitTest', 'Serialize', function() { ...@@ -506,3 +506,31 @@ TEST_F('CvoxSpannableUnitTest', 'Serialize', function() {
assertThat(freshNonStatelessSerializable, assertThat(freshNonStatelessSerializable,
eqJSON(thawnNonStatelessSerializable)); eqJSON(thawnNonStatelessSerializable));
}); });
TEST_F('CvoxSpannableUnitTest', 'GetSpanIntervals', function() {
function Foo() {}
function Bar() {}
var ms = new MultiSpannable('f12b45f78b01');
var foo = new Foo();
var bar = new Bar();
ms.setSpan(foo, 0, 3);
ms.setSpan(bar, 3, 6);
ms.setSpan(foo, 6, 9);
ms.setSpan(bar, 9, 12);
assertEquals(2, ms.getSpansInstanceOf(Foo).length);
assertEquals(2, ms.getSpansInstanceOf(Bar).length);
var fooIntervals = ms.getSpanIntervals(foo);
assertEquals(2, fooIntervals.length);
assertEquals(0, fooIntervals[0].start);
assertEquals(3, fooIntervals[0].end);
assertEquals(6, fooIntervals[1].start);
assertEquals(9, fooIntervals[1].end);
var barIntervals = ms.getSpanIntervals(bar);
assertEquals(2, barIntervals.length);
assertEquals(3, barIntervals[0].start);
assertEquals(6, barIntervals[0].end);
assertEquals(9, barIntervals[1].start);
assertEquals(12, barIntervals[1].end);
});
...@@ -635,7 +635,6 @@ Background.prototype = { ...@@ -635,7 +635,6 @@ Background.prototype = {
brailleRoutingCommand_: function(text, position) { brailleRoutingCommand_: function(text, position) {
var actionNodeSpan = null; var actionNodeSpan = null;
var selectionSpan = null; var selectionSpan = null;
var selSpans = text.getSpansInstanceOf(Output.SelectionSpan); var selSpans = text.getSpansInstanceOf(Output.SelectionSpan);
var nodeSpans = text.getSpansInstanceOf(Output.NodeSpan); var nodeSpans = text.getSpansInstanceOf(Output.NodeSpan);
for (var i = 0, selSpan; selSpan = selSpans[i]; i++) { for (var i = 0, selSpan; selSpan = selSpans[i]; i++) {
...@@ -646,14 +645,22 @@ Background.prototype = { ...@@ -646,14 +645,22 @@ Background.prototype = {
} }
} }
var interval;
for (var j = 0, nodeSpan; nodeSpan = nodeSpans[j]; j++) { for (var j = 0, nodeSpan; nodeSpan = nodeSpans[j]; j++) {
if (text.getSpanStart(nodeSpan) <= position && var intervals = text.getSpanIntervals(nodeSpan);
position <= text.getSpanEnd(nodeSpan)) var tempInterval = intervals.find(function(innerInterval) {
return innerInterval.start <= position &&
position <= innerInterval.end;
});
if (tempInterval) {
actionNodeSpan = nodeSpan; actionNodeSpan = nodeSpan;
interval = tempInterval;
}
} }
if (!actionNodeSpan) if (!actionNodeSpan)
return; return;
var actionNode = actionNodeSpan.node; var actionNode = actionNodeSpan.node;
var offset = actionNodeSpan.offset; var offset = actionNodeSpan.offset;
if (actionNode.role === RoleType.INLINE_TEXT_BOX) if (actionNode.role === RoleType.INLINE_TEXT_BOX)
...@@ -667,10 +674,9 @@ Background.prototype = { ...@@ -667,10 +674,9 @@ Background.prototype = {
if (!selectionSpan) if (!selectionSpan)
selectionSpan = actionNodeSpan; selectionSpan = actionNodeSpan;
var start = text.getSpanStart(selectionSpan);
var targetPosition = position - start + offset;
if (actionNode.state.richlyEditable) { if (actionNode.state.richlyEditable) {
var start = interval ? interval.start : text.getSpanStart(selectionSpan);
var targetPosition = position - start + offset;
chrome.automation.setDocumentSelection({ chrome.automation.setDocumentSelection({
anchorObject: actionNode, anchorObject: actionNode,
anchorOffset: targetPosition, anchorOffset: targetPosition,
...@@ -678,6 +684,8 @@ Background.prototype = { ...@@ -678,6 +684,8 @@ Background.prototype = {
focusOffset: targetPosition focusOffset: targetPosition
}); });
} else { } else {
var start = text.getSpanStart(selectionSpan);
var targetPosition = position - start + offset;
actionNode.setSelection(targetPosition, targetPosition); actionNode.setSelection(targetPosition, targetPosition);
} }
}, },
......
...@@ -530,7 +530,7 @@ AutomationRichEditableText.prototype = { ...@@ -530,7 +530,7 @@ AutomationRichEditableText.prototype = {
/** @private */ /** @private */
brailleCurrentRichLine_: function() { brailleCurrentRichLine_: function() {
var cur = this.line_; var cur = this.line_;
var value = new Spannable(cur.value_); var value = new MultiSpannable(cur.value_);
if (!this.node_.constructor) if (!this.node_.constructor)
return; return;
value.getSpansInstanceOf(this.node_.constructor).forEach(function(span) { value.getSpansInstanceOf(this.node_.constructor).forEach(function(span) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment