use of android.text.style.LeadingMarginSpan in project android_frameworks_base by ParanoidAndroid.
the class Layout method drawText.
/**
* @hide
*/
public void drawText(Canvas canvas, int firstLine, int lastLine) {
int previousLineBottom = getLineTop(firstLine);
int previousLineEnd = getLineStart(firstLine);
ParagraphStyle[] spans = NO_PARA_SPANS;
int spanEnd = 0;
TextPaint paint = mPaint;
CharSequence buf = mText;
Alignment paraAlign = mAlignment;
TabStops tabStops = null;
boolean tabStopsIsInitialized = false;
TextLine tl = TextLine.obtain();
// The baseline is the top of the following line minus the current line's descent.
for (int i = firstLine; i <= lastLine; i++) {
int start = previousLineEnd;
previousLineEnd = getLineStart(i + 1);
int end = getLineVisibleEnd(i, start, previousLineEnd);
int ltop = previousLineBottom;
int lbottom = getLineTop(i + 1);
previousLineBottom = lbottom;
int lbaseline = lbottom - getLineDescent(i);
int dir = getParagraphDirection(i);
int left = 0;
int right = mWidth;
if (mSpannedText) {
Spanned sp = (Spanned) buf;
int textLength = buf.length();
boolean isFirstParaLine = (start == 0 || buf.charAt(start - 1) == '\n');
// our problem.
if (start >= spanEnd && (i == firstLine || isFirstParaLine)) {
spanEnd = sp.nextSpanTransition(start, textLength, ParagraphStyle.class);
spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
paraAlign = mAlignment;
for (int n = spans.length - 1; n >= 0; n--) {
if (spans[n] instanceof AlignmentSpan) {
paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
break;
}
}
tabStopsIsInitialized = false;
}
// Draw all leading margin spans. Adjust left or right according
// to the paragraph direction of the line.
final int length = spans.length;
for (int n = 0; n < length; n++) {
if (spans[n] instanceof LeadingMarginSpan) {
LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
boolean useFirstLineMargin = isFirstParaLine;
if (margin instanceof LeadingMarginSpan2) {
int count = ((LeadingMarginSpan2) margin).getLeadingMarginLineCount();
int startLine = getLineForOffset(sp.getSpanStart(margin));
useFirstLineMargin = i < startLine + count;
}
if (dir == DIR_RIGHT_TO_LEFT) {
margin.drawLeadingMargin(canvas, paint, right, dir, ltop, lbaseline, lbottom, buf, start, end, isFirstParaLine, this);
right -= margin.getLeadingMargin(useFirstLineMargin);
} else {
margin.drawLeadingMargin(canvas, paint, left, dir, ltop, lbaseline, lbottom, buf, start, end, isFirstParaLine, this);
left += margin.getLeadingMargin(useFirstLineMargin);
}
}
}
}
boolean hasTabOrEmoji = getLineContainsTab(i);
// Can't tell if we have tabs for sure, currently
if (hasTabOrEmoji && !tabStopsIsInitialized) {
if (tabStops == null) {
tabStops = new TabStops(TAB_INCREMENT, spans);
} else {
tabStops.reset(TAB_INCREMENT, spans);
}
tabStopsIsInitialized = true;
}
// Determine whether the line aligns to normal, opposite, or center.
Alignment align = paraAlign;
if (align == Alignment.ALIGN_LEFT) {
align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
} else if (align == Alignment.ALIGN_RIGHT) {
align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
}
int x;
if (align == Alignment.ALIGN_NORMAL) {
if (dir == DIR_LEFT_TO_RIGHT) {
x = left;
} else {
x = right;
}
} else {
int max = (int) getLineExtent(i, tabStops, false);
if (align == Alignment.ALIGN_OPPOSITE) {
if (dir == DIR_LEFT_TO_RIGHT) {
x = right - max;
} else {
x = left - max;
}
} else {
// Alignment.ALIGN_CENTER
max = max & ~1;
x = (right + left - max) >> 1;
}
}
Directions directions = getLineDirections(i);
if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) {
// XXX: assumes there's nothing additional to be done
canvas.drawText(buf, start, end, x, lbaseline, paint);
} else {
tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops);
tl.draw(canvas, x, ltop, lbaseline, lbottom);
}
}
TextLine.recycle(tl);
}
use of android.text.style.LeadingMarginSpan in project android_frameworks_base by ParanoidAndroid.
the class Layout method getParagraphLeadingMargin.
/**
* Returns the effective leading margin (unsigned) for this line,
* taking into account LeadingMarginSpan and LeadingMarginSpan2.
* @param line the line index
* @return the leading margin of this line
*/
private int getParagraphLeadingMargin(int line) {
if (!mSpannedText) {
return 0;
}
Spanned spanned = (Spanned) mText;
int lineStart = getLineStart(line);
int lineEnd = getLineEnd(line);
int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, LeadingMarginSpan.class);
LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, LeadingMarginSpan.class);
if (spans.length == 0) {
// no leading margin span;
return 0;
}
int margin = 0;
boolean isFirstParaLine = lineStart == 0 || spanned.charAt(lineStart - 1) == '\n';
for (int i = 0; i < spans.length; i++) {
LeadingMarginSpan span = spans[i];
boolean useFirstLineMargin = isFirstParaLine;
if (span instanceof LeadingMarginSpan2) {
int spStart = spanned.getSpanStart(span);
int spanLine = getLineForOffset(spStart);
int count = ((LeadingMarginSpan2) span).getLeadingMarginLineCount();
useFirstLineMargin = line < spanLine + count;
}
margin += span.getLeadingMargin(useFirstLineMargin);
}
return margin;
}
use of android.text.style.LeadingMarginSpan in project platform_frameworks_base by android.
the class Layout method drawText.
/**
* @hide
*/
public void drawText(Canvas canvas, int firstLine, int lastLine) {
int previousLineBottom = getLineTop(firstLine);
int previousLineEnd = getLineStart(firstLine);
ParagraphStyle[] spans = NO_PARA_SPANS;
int spanEnd = 0;
TextPaint paint = mPaint;
CharSequence buf = mText;
Alignment paraAlign = mAlignment;
TabStops tabStops = null;
boolean tabStopsIsInitialized = false;
TextLine tl = TextLine.obtain();
// The baseline is the top of the following line minus the current line's descent.
for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) {
int start = previousLineEnd;
previousLineEnd = getLineStart(lineNum + 1);
int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
int ltop = previousLineBottom;
int lbottom = getLineTop(lineNum + 1);
previousLineBottom = lbottom;
int lbaseline = lbottom - getLineDescent(lineNum);
int dir = getParagraphDirection(lineNum);
int left = 0;
int right = mWidth;
if (mSpannedText) {
Spanned sp = (Spanned) buf;
int textLength = buf.length();
boolean isFirstParaLine = (start == 0 || buf.charAt(start - 1) == '\n');
// our problem.
if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) {
spanEnd = sp.nextSpanTransition(start, textLength, ParagraphStyle.class);
spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
paraAlign = mAlignment;
for (int n = spans.length - 1; n >= 0; n--) {
if (spans[n] instanceof AlignmentSpan) {
paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
break;
}
}
tabStopsIsInitialized = false;
}
// Draw all leading margin spans. Adjust left or right according
// to the paragraph direction of the line.
final int length = spans.length;
boolean useFirstLineMargin = isFirstParaLine;
for (int n = 0; n < length; n++) {
if (spans[n] instanceof LeadingMarginSpan2) {
int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount();
int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
// the count that is greatest
if (lineNum < startLine + count) {
useFirstLineMargin = true;
break;
}
}
}
for (int n = 0; n < length; n++) {
if (spans[n] instanceof LeadingMarginSpan) {
LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
if (dir == DIR_RIGHT_TO_LEFT) {
margin.drawLeadingMargin(canvas, paint, right, dir, ltop, lbaseline, lbottom, buf, start, end, isFirstParaLine, this);
right -= margin.getLeadingMargin(useFirstLineMargin);
} else {
margin.drawLeadingMargin(canvas, paint, left, dir, ltop, lbaseline, lbottom, buf, start, end, isFirstParaLine, this);
left += margin.getLeadingMargin(useFirstLineMargin);
}
}
}
}
boolean hasTab = getLineContainsTab(lineNum);
// Can't tell if we have tabs for sure, currently
if (hasTab && !tabStopsIsInitialized) {
if (tabStops == null) {
tabStops = new TabStops(TAB_INCREMENT, spans);
} else {
tabStops.reset(TAB_INCREMENT, spans);
}
tabStopsIsInitialized = true;
}
// Determine whether the line aligns to normal, opposite, or center.
Alignment align = paraAlign;
if (align == Alignment.ALIGN_LEFT) {
align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
} else if (align == Alignment.ALIGN_RIGHT) {
align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
}
int x;
if (align == Alignment.ALIGN_NORMAL) {
if (dir == DIR_LEFT_TO_RIGHT) {
x = left + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
} else {
x = right + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
}
} else {
int max = (int) getLineExtent(lineNum, tabStops, false);
if (align == Alignment.ALIGN_OPPOSITE) {
if (dir == DIR_LEFT_TO_RIGHT) {
x = right - max + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT);
} else {
x = left - max + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT);
}
} else {
// Alignment.ALIGN_CENTER
max = max & ~1;
x = ((right + left - max) >> 1) + getIndentAdjust(lineNum, Alignment.ALIGN_CENTER);
}
}
paint.setHyphenEdit(getHyphen(lineNum));
Directions directions = getLineDirections(lineNum);
if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab) {
// XXX: assumes there's nothing additional to be done
canvas.drawText(buf, start, end, x, lbaseline, paint);
} else {
tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
tl.draw(canvas, x, ltop, lbaseline, lbottom);
}
paint.setHyphenEdit(0);
}
TextLine.recycle(tl);
}
use of android.text.style.LeadingMarginSpan in project platform_frameworks_base by android.
the class Layout method measurePara.
/* package */
static float measurePara(TextPaint paint, CharSequence text, int start, int end) {
MeasuredText mt = MeasuredText.obtain();
TextLine tl = TextLine.obtain();
try {
mt.setPara(text, start, end, TextDirectionHeuristics.LTR, null);
Directions directions;
int dir;
if (mt.mEasy) {
directions = DIRS_ALL_LEFT_TO_RIGHT;
dir = Layout.DIR_LEFT_TO_RIGHT;
} else {
directions = AndroidBidi.directions(mt.mDir, mt.mLevels, 0, mt.mChars, 0, mt.mLen);
dir = mt.mDir;
}
char[] chars = mt.mChars;
int len = mt.mLen;
boolean hasTabs = false;
TabStops tabStops = null;
// leading margins should be taken into account when measuring a paragraph
int margin = 0;
if (text instanceof Spanned) {
Spanned spanned = (Spanned) text;
LeadingMarginSpan[] spans = getParagraphSpans(spanned, start, end, LeadingMarginSpan.class);
for (LeadingMarginSpan lms : spans) {
margin += lms.getLeadingMargin(true);
}
}
for (int i = 0; i < len; ++i) {
if (chars[i] == '\t') {
hasTabs = true;
if (text instanceof Spanned) {
Spanned spanned = (Spanned) text;
int spanEnd = spanned.nextSpanTransition(start, end, TabStopSpan.class);
TabStopSpan[] spans = getParagraphSpans(spanned, start, spanEnd, TabStopSpan.class);
if (spans.length > 0) {
tabStops = new TabStops(TAB_INCREMENT, spans);
}
}
break;
}
}
tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops);
return margin + tl.metrics(null);
} finally {
TextLine.recycle(tl);
MeasuredText.recycle(mt);
}
}
use of android.text.style.LeadingMarginSpan in project TextJustify-Android by bluejamesbond.
the class SpannableDocumentLayout method onMeasure.
@SuppressWarnings("ConstantConditions")
@Override
public boolean onMeasure(IProgress<Float> progress, ICancel<Boolean> cancelled) {
boolean done = true;
float parentWidth = params.getParentWidth();
float boundWidth = params.getParentWidth() - params.getInsetPaddingLeft() - params.getInsetPaddingRight();
mLeadMarginSpanDrawEvents = new LinkedList<>();
StaticLayout staticLayout = new StaticLayout(getText(), (TextPaint) getPaint(), (int) boundWidth, Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
int[] newTokens = new int[TOKEN_LENGTH * 1000];
LeadingMarginSpan[] activeLeadSpans = new LeadingMarginSpan[0];
HashMap<LeadingMarginSpan, Integer> leadSpans = new HashMap<>();
TextAlignment defAlign = params.textAlignment;
Spannable textCpy = (Spannable) this.text;
Paint.FontMetricsInt fmi = paint.getFontMetricsInt();
int maxTextIndex = textCpy.length() - 1;
int lines = staticLayout.getLineCount();
int enableLineBreak = 0;
int index = 0;
int lineNumber;
float x;
float y = params.insetPaddingTop;
float left = params.insetPaddingLeft;
float right = params.insetPaddingRight;
float lineHeightAdd = params.lineHeightMultiplier;
float lastAscent;
float lastDescent;
boolean isParaStart = true;
boolean isReverse = params.reverse;
for (lineNumber = 0; lineNumber < lines; lineNumber++) {
if (cancelled.isCancelled()) {
done = false;
break;
}
progress.onUpdate((float) lineNumber / (float) lines);
newTokens = ammortizeArray(newTokens, index);
int start = staticLayout.getLineStart(lineNumber);
int end = staticLayout.getLineEnd(lineNumber);
float realWidth = boundWidth;
if (params.debugging) {
Console.log(start + " => " + end + " :: " + " " + -staticLayout.getLineAscent(lineNumber) + " " + staticLayout.getLineDescent(lineNumber) + " " + textCpy.subSequence(start, end).toString());
}
// start == end => end of textCpy
if (start == end || lineNumber >= params.maxLines) {
break;
}
// Get textCpy alignment for the line
TextAlignmentSpan[] textAlignmentSpans = textCpy.getSpans(start, end, TextAlignmentSpan.class);
TextAlignment lineTextAlignment = textAlignmentSpans.length == 0 ? defAlign : textAlignmentSpans[0].getTextAlignment();
// Calculate components of line height
lastAscent = -staticLayout.getLineAscent(lineNumber);
lastDescent = staticLayout.getLineDescent(lineNumber) + lineHeightAdd;
// Handle reverse
DirectionSpan[] directionSpans = textCpy.getSpans(start, end, DirectionSpan.class);
isReverse = directionSpans.length > 0 ? directionSpans[0].isReverse() : params.reverse;
// Line is ONLY a <br/> or \n
if (start + 1 == end && (Character.getNumericValue(textCpy.charAt(start)) == -1 || textCpy.charAt(start) == '\n')) {
// Line break indicates a new paragraph
// is next
isParaStart = true;
// Use the line-height of the next line
if (lineNumber + 1 < lines) {
y += enableLineBreak * (-staticLayout.getLineAscent(lineNumber + 1) + staticLayout.getLineDescent(lineNumber + 1));
}
// Don't ignore the next line breaks
enableLineBreak = 1;
continue;
} else {
// Ignore the next line break
enableLineBreak = 0;
}
x = lineTextAlignment == TextAlignment.RIGHT ? right : left;
y += lastAscent;
// Line CONTAINS a \n
boolean isParaEnd = end == maxTextIndex || textCpy.charAt(Math.min(end, maxTextIndex)) == '\n' || textCpy.charAt(end - 1) == '\n';
if (isParaEnd) {
enableLineBreak = 1;
}
// LeadingMarginSpan block
if (isParaStart) {
// Process LeadingMarginSpan
activeLeadSpans = textCpy.getSpans(start, end, LeadingMarginSpan.class);
// Set up all the spans
if (activeLeadSpans.length > 0) {
for (LeadingMarginSpan leadSpan : activeLeadSpans) {
if (!leadSpans.containsKey(leadSpan)) {
// Default margin is everything
int marginLineCount = -1;
if (leadSpan instanceof LeadingMarginSpan.LeadingMarginSpan2) {
LeadingMarginSpan.LeadingMarginSpan2 leadSpan2 = ((LeadingMarginSpan.LeadingMarginSpan2) leadSpan);
marginLineCount = leadSpan2.getLeadingMarginLineCount();
}
leadSpans.put(leadSpan, marginLineCount);
}
}
}
}
float totalMargin = 0.0f;
int top = (int) (y - lastAscent);
int baseline = (int) (y);
int bottom = (int) (y + lastDescent);
for (LeadingMarginSpan leadSpan : activeLeadSpans) {
// TOKEN_X based on alignment
float calcX = x;
// LineAlignment
int lineAlignmentVal = 1;
if (lineTextAlignment == TextAlignment.RIGHT) {
lineAlignmentVal = -1;
calcX = parentWidth - x;
}
// Get current line count
int spanLines = leadSpans.get(leadSpan);
// Update only if the valid next valid
if (spanLines > 0 || spanLines == -1) {
leadSpans.put(leadSpan, spanLines == -1 ? -1 : spanLines - 1);
mLeadMarginSpanDrawEvents.add(new LeadingMarginSpanDrawParameters(leadSpan, (int) calcX, lineAlignmentVal, top, baseline, bottom, start, end, isParaStart));
// Is margin required?
totalMargin += leadSpan.getLeadingMargin(isParaStart);
}
}
x += totalMargin;
realWidth -= totalMargin;
// Disable/enable new paragraph
isParaStart = isParaEnd;
// TextAlignmentSpan block
if (isParaEnd && lineTextAlignment == TextAlignment.JUSTIFIED) {
lineTextAlignment = isReverse ? TextAlignment.RIGHT : TextAlignment.LEFT;
}
if (params.debugging) {
Console.log(String.format("Align: %s, X: %fpx, Y: %fpx, PWidth: %fpx", lineTextAlignment, x, y, parentWidth));
}
switch(lineTextAlignment) {
case RIGHT:
{
float lineWidth = Styled.measureText(paint, workPaint, textCpy, start, end, fmi);
index = pushToken(newTokens, index, start, end, parentWidth - x - lineWidth, y, lastAscent, lastDescent, lineNumber);
y += lastDescent;
continue;
}
case CENTER:
{
float lineWidth = Styled.measureText(paint, workPaint, textCpy, start, end, fmi);
index = pushToken(newTokens, index, start, end, x + (realWidth - lineWidth) / 2, y, lastAscent, lastDescent, lineNumber);
y += lastDescent;
continue;
}
case LEFT:
{
index = pushToken(newTokens, index, start, end, x, y, lastAscent, lastDescent, lineNumber);
y += lastDescent;
continue;
}
}
// FIXME: Space at the end of each line, possibly due to scrollbar offset
// LinkedList<Integer> tokenized = tokenize(textCpy, start, end - 1);
// FIXME: 2016/4/16 Issue #105 bug
LinkedList<Integer> tokenized = tokenize(textCpy, start, end);
// If one long word without any spaces
if (tokenized.size() == 1) {
int stop = tokenized.get(0);
// characters individually
if (getTrimmedLength(textCpy, start, stop) != 0) {
float[] textWidths = new float[stop - start];
float sum = 0.0f, textsOffset = 0.0f, offset;
int m = 0;
Styled.getTextWidths(paint, workPaint, textCpy, start, stop, textWidths, fmi);
for (float tw : textWidths) {
sum += tw;
}
offset = (realWidth - sum) / (textWidths.length - 1);
for (int k = start; k < stop; k++) {
index = pushToken(newTokens, index, k, k + 1, x + textsOffset + (offset * m), y, lastAscent, lastDescent, lineNumber);
newTokens = ammortizeArray(newTokens, index);
textsOffset += textWidths[m++];
}
}
} else // Handle multiple words
{
int m = 1;
int indexOffset = 0;
int startIndex = index;
int reqSpaces = (tokenized.size() - 1) * TOKEN_LENGTH;
int rtlZero = 0;
float rtlRight = 0;
float rtlMul = 1;
float lineWidth = 0;
float offset;
if (isReverse) {
indexOffset = -2 * TOKEN_LENGTH;
rtlRight = parentWidth;
rtlMul = -1;
rtlZero = 1;
// reverse index
index += reqSpaces;
}
// more space
newTokens = ammortizeArray(newTokens, index + reqSpaces);
for (int stop : tokenized) {
float wordWidth = Styled.measureText(paint, workPaint, textCpy, start, stop, fmi);
// add word
index = pushToken(newTokens, index, start, stop, rtlRight + rtlMul * (x + lineWidth + rtlZero * wordWidth), y, lastAscent, lastDescent, lineNumber);
lineWidth += wordWidth;
start = stop + 1;
// based on if rtl
index += indexOffset;
}
if (isReverse) {
index = startIndex + reqSpaces + TOKEN_LENGTH;
}
offset = (realWidth - lineWidth) / (float) (tokenized.size() - 1);
if (isReverse) {
for (int pos = index - TOKEN_LENGTH * 2; pos >= startIndex; pos -= TOKEN_LENGTH) {
newTokens[pos + TOKEN_X] = (int) (((float) newTokens[pos + TOKEN_X]) - (offset * (float) m++));
}
} else {
for (int pos = startIndex + TOKEN_LENGTH; pos < index; pos += TOKEN_LENGTH) {
newTokens[pos + TOKEN_X] = (int) (((float) newTokens[pos + TOKEN_X]) + (offset * (float) m++));
}
}
}
y += lastDescent;
}
lineCount = lineNumber;
tokens = newTokens;
params.changed = false;
textChange = !done;
measuredHeight = (int) (y - lineHeightAdd + params.insetPaddingBottom);
return done;
}
Aggregations