layout/relayout with different widths and justification
Change-Id: I2f6d9121a97d3dc12981e0aaf420c56a8f7e05eb Bug: skia:9851, skia:9875 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/271536 Commit-Queue: Julia Lavrova <jlavrova@google.com> Reviewed-by: Ben Wagner <bungeman@google.com>
This commit is contained in:
parent
5a5fe79ebf
commit
212bf079de
@ -131,8 +131,17 @@ void ParagraphImpl::layout(SkScalar rawWidth) {
|
||||
this->fRunShifts.reset();
|
||||
this->fClusters.reset();
|
||||
} else if (fState >= kLineBroken && (fOldWidth != floorWidth || fOldHeight != fHeight)) {
|
||||
// We can use the results from SkShaper but have to break lines again
|
||||
// We can use the results from SkShaper but have to do EVERYTHING ELSE again
|
||||
this->fClusters.reset();
|
||||
this->fRunShifts.reset();
|
||||
this->resetRunShifts();
|
||||
fState = kShaped;
|
||||
|
||||
this->buildClusterTable();
|
||||
fState = kClusterized;
|
||||
|
||||
this->markLineBreaks(); // Just because it's on cluster table
|
||||
fState = kMarked;
|
||||
}
|
||||
|
||||
if (fState < kShaped) {
|
||||
@ -165,6 +174,7 @@ void ParagraphImpl::layout(SkScalar rawWidth) {
|
||||
return;
|
||||
}
|
||||
if (fState < kShaped) {
|
||||
this->resetRunShifts();
|
||||
fState = kShaped;
|
||||
} else {
|
||||
layout(floorWidth);
|
||||
@ -434,9 +444,6 @@ void ParagraphImpl::formatLines(SkScalar maxWidth) {
|
||||
fLines.reset();
|
||||
return;
|
||||
}
|
||||
if (effectiveAlign == TextAlign::kJustify) {
|
||||
this->resetRunShifts();
|
||||
}
|
||||
|
||||
for (auto& line : fLines) {
|
||||
if (&line == &fLines.back() && effectiveAlign == TextAlign::kJustify) {
|
||||
@ -1073,9 +1080,17 @@ Block& ParagraphImpl::block(BlockIndex blockIndex) {
|
||||
|
||||
// TODO: Cache this information
|
||||
void ParagraphImpl::resetRunShifts() {
|
||||
fRunShifts.resize(fRuns.size());
|
||||
|
||||
if (fRunShifts.empty()) {
|
||||
fRunShifts.resize(fRuns.size());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fRuns.size(); ++i) {
|
||||
fRunShifts[i].fShifts.push_back_n(fRuns[i].size() + 1, 0.0);
|
||||
auto& run = fRuns[i];
|
||||
auto& shifts = fRunShifts[i];
|
||||
run.resetShifts();
|
||||
shifts.fShifts.reset();
|
||||
shifts.fShifts.push_back_n(run.size() + 1, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -334,7 +334,7 @@ SkScalar Cluster::trimmedWidth(size_t pos) const {
|
||||
// Find the width until the pos and return the min between trimmedWidth and the width(pos)
|
||||
// We don't have to take in account cluster shift since it's the same for 0 and for pos
|
||||
auto& run = fMaster->run(fRunIndex);
|
||||
return std::min(run.positionX(pos) - run.positionX(fStart), fWidth - fSpacing);
|
||||
return std::min(run.positionX(pos) - run.positionX(fStart), fWidth);
|
||||
}
|
||||
|
||||
SkScalar Run::positionX(size_t pos) const {
|
||||
|
@ -163,6 +163,11 @@ public:
|
||||
void commit();
|
||||
|
||||
SkRect getBounds(size_t pos) const { return fBounds[pos]; }
|
||||
|
||||
void resetShifts() {
|
||||
for (auto& r: fShifts) { r = 0; }
|
||||
fSpaced = false;
|
||||
}
|
||||
private:
|
||||
friend class ParagraphImpl;
|
||||
friend class TextLine;
|
||||
|
@ -136,7 +136,7 @@ SkRect TextLine::calculateBoundaries() {
|
||||
}
|
||||
clusterShift += cluster->width();
|
||||
for (auto i = cluster->startPos(); i < cluster->endPos(); ++i) {
|
||||
auto posX = run->posX(i);
|
||||
auto posX = run->positionX(i);
|
||||
auto posY = run->posY(i);
|
||||
auto bounds = run->getBounds(i);
|
||||
bounds.offset(posX + runShift, posY);
|
||||
@ -724,6 +724,9 @@ TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
|
||||
result.clip.offset(textStartInLine, 0);
|
||||
|
||||
if (compareRound(result.clip.fRight, fAdvance.fX) > 0 && !includeGhostSpaces) {
|
||||
// There are few cases when we need it.
|
||||
// The most important one: we measure the text with spaces at the end
|
||||
// and we should ignore these spaces
|
||||
result.clippingNeeded = true;
|
||||
result.clip.fRight = fAdvance.fX;
|
||||
}
|
||||
|
@ -47,7 +47,13 @@ void TextWrapper::lookAhead(SkScalar maxWidth, Cluster* endOfClusters) {
|
||||
if (nextWordLength > maxWidth) {
|
||||
// If the word is too long we can break it right now and hope it's enough
|
||||
fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, nextWordLength);
|
||||
fTooLongWord = true;
|
||||
if (fClusters.endPos() - fClusters.startPos() > 1 ||
|
||||
fWords.empty()) {
|
||||
fTooLongWord = true;
|
||||
} else {
|
||||
// Even if the word is too long there is a very little space on this line.
|
||||
// let's deal with it on the next line.
|
||||
}
|
||||
}
|
||||
|
||||
if (cluster->width() > maxWidth) {
|
||||
@ -255,6 +261,7 @@ void TextWrapper::breakTextIntoLines(ParagraphImpl* parent,
|
||||
needEllipsis && !fHardLineBreak);
|
||||
|
||||
softLineMaxIntrinsicWidth += widthWithSpaces;
|
||||
|
||||
fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
|
||||
if (fHardLineBreak) {
|
||||
softLineMaxIntrinsicWidth = 0;
|
||||
@ -283,6 +290,7 @@ void TextWrapper::breakTextIntoLines(ParagraphImpl* parent,
|
||||
}
|
||||
|
||||
// We finished formatting the text but we need to scan the rest for some numbers
|
||||
// TODO: make it a case of a normal flow
|
||||
if (fEndLine.endCluster() != nullptr) {
|
||||
auto lastWordLength = 0.0f;
|
||||
auto cluster = fEndLine.endCluster();
|
||||
@ -291,7 +299,6 @@ void TextWrapper::breakTextIntoLines(ParagraphImpl* parent,
|
||||
if (cluster->isHardBreak()) {
|
||||
fMaxIntrinsicWidth = std::max(fMaxIntrinsicWidth, softLineMaxIntrinsicWidth);
|
||||
softLineMaxIntrinsicWidth = 0;
|
||||
|
||||
fMinIntrinsicWidth = std::max(fMinIntrinsicWidth, lastWordLength);
|
||||
lastWordLength = 0;
|
||||
} else if (cluster->isWhitespaces()) {
|
||||
|
@ -2097,6 +2097,117 @@ private:
|
||||
typedef Sample INHERITED;
|
||||
};
|
||||
|
||||
class ParagraphView28 : public ParagraphView_Base {
|
||||
protected:
|
||||
SkString name() override { return SkString("Paragraph28"); }
|
||||
|
||||
void onDrawContent(SkCanvas* canvas) override {
|
||||
|
||||
const char* text = "AAAAA BBBBB CCCCC DDDDD EEEEE FFFFF GGGGG HHHHH IIIII JJJJJ KKKKK LLLLL MMMMM NNNNN OOOOO PPPPP QQQQQ";
|
||||
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
ParagraphStyle paragraph_style;
|
||||
paragraph_style.setTextAlign(TextAlign::kJustify);
|
||||
auto collection = getFontCollection();
|
||||
ParagraphBuilderImpl builder(paragraph_style, collection);
|
||||
TextStyle text_style;
|
||||
text_style.setColor(SK_ColorBLACK);
|
||||
text_style.setFontFamilies({SkString("Roboto")});
|
||||
text_style.setFontSize(40);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText(text);
|
||||
auto paragraph = builder.Build();
|
||||
auto s = 186;
|
||||
paragraph->layout(360 - s);
|
||||
paragraph->paint(canvas, 0, 0);
|
||||
/*
|
||||
paragraph->layout(360);
|
||||
paragraph->paint(canvas, 0, 0);
|
||||
canvas->translate(0, 400);
|
||||
paragraph->layout(354.333);
|
||||
paragraph->paint(canvas, 0, 0);
|
||||
*/
|
||||
}
|
||||
|
||||
private:
|
||||
typedef Sample INHERITED;
|
||||
};
|
||||
|
||||
class ParagraphView29 : public ParagraphView_Base {
|
||||
protected:
|
||||
SkString name() override { return SkString("Paragraph29"); }
|
||||
|
||||
void onDrawContent(SkCanvas* canvas) override {
|
||||
|
||||
const char* text = "PESTO";
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorRED);
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(1);
|
||||
|
||||
ParagraphStyle paragraph_style;
|
||||
paragraph_style.setTextAlign(TextAlign::kCenter);
|
||||
auto collection = getFontCollection();
|
||||
ParagraphBuilderImpl builder(paragraph_style, collection);
|
||||
TextStyle text_style;
|
||||
text_style.setColor(SK_ColorBLACK);
|
||||
text_style.setFontFamilies({SkString("Roboto")});
|
||||
text_style.setFontSize(48);
|
||||
text_style.setFontStyle(SkFontStyle::Bold());
|
||||
text_style.setLetterSpacing(3);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText(text);
|
||||
auto paragraph = builder.Build();
|
||||
auto w = width() / 2;
|
||||
paragraph->layout(w);
|
||||
paragraph->paint(canvas, 0, 0);
|
||||
canvas->drawRect(SkRect::MakeXYWH(0, 0, width() / 2, paragraph->getHeight()), paint);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef Sample INHERITED;
|
||||
};
|
||||
|
||||
class ParagraphView30 : public ParagraphView_Base {
|
||||
protected:
|
||||
SkString name() override { return SkString("Paragraph30"); }
|
||||
|
||||
void onDrawContent(SkCanvas* canvas) override {
|
||||
|
||||
const char* text = "test text with space at end ";
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
|
||||
ParagraphStyle paragraph_style;
|
||||
paragraph_style.setTextAlign(TextAlign::kCenter);
|
||||
auto collection = getFontCollection();
|
||||
TextStyle text_style;
|
||||
text_style.setColor(SK_ColorBLACK);
|
||||
text_style.setFontFamilies({SkString("Roboto")});
|
||||
text_style.setFontSize(48);
|
||||
|
||||
ParagraphBuilderImpl builder(paragraph_style, collection);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText(text);
|
||||
auto paragraph = builder.Build();
|
||||
|
||||
auto width = this->width();
|
||||
paragraph->layout(width);
|
||||
SkDebugf("%f: %f %f\n", width, paragraph->getMinIntrinsicWidth(), paragraph->getMaxIntrinsicWidth());
|
||||
paragraph->paint(canvas, 0, 0);
|
||||
auto boxes = paragraph->getRectsForRange(0, 1, RectHeightStyle::kTight, RectWidthStyle::kTight);
|
||||
for (auto& b : boxes) {
|
||||
SkDebugf("box[%f:%f * %f:%f] %s\n",
|
||||
b.rect.fLeft, b.rect.fRight, b.rect.fTop, b.rect.fBottom,
|
||||
b.direction == TextDirection::kRtl ? "rtl" : "ltr");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef Sample INHERITED;
|
||||
};
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DEF_SAMPLE(return new ParagraphView1();)
|
||||
@ -2123,5 +2234,8 @@ DEF_SAMPLE(return new ParagraphView22();)
|
||||
DEF_SAMPLE(return new ParagraphView23();)
|
||||
DEF_SAMPLE(return new ParagraphView24();)
|
||||
DEF_SAMPLE(return new ParagraphView25();)
|
||||
//DEF_SAMPLE(return new ParagraphView26();)
|
||||
//DEF_SAMPLE(return new ParagraphView27();)
|
||||
DEF_SAMPLE(return new ParagraphView26();)
|
||||
DEF_SAMPLE(return new ParagraphView27();)
|
||||
DEF_SAMPLE(return new ParagraphView28();)
|
||||
DEF_SAMPLE(return new ParagraphView29();)
|
||||
DEF_SAMPLE(return new ParagraphView30();)
|
||||
|
Loading…
Reference in New Issue
Block a user