Bug 1712703 - Record the "hangable" advance of trailing whitespace in a textframe property, and use this in nsLineLayout to adjust inline positioning. r=dholbert

Differential Revision: https://phabricator.services.mozilla.com/D174728
This commit is contained in:
Jonathan Kew 2023-04-05 20:28:08 +00:00
Родитель c59eae6aaf
Коммит 1c77c30705
4 изменённых файлов: 94 добавлений и 9 удалений

Просмотреть файл

@ -3024,6 +3024,36 @@ void nsLineLayout::ExpandInlineRubyBoxes(PerSpanData* aSpan) {
}
}
nscoord nsLineLayout::GetHangFrom(const PerSpanData* aSpan, bool aLineIsRTL) {
const PerFrameData* pfd = aSpan->mLastFrame;
nscoord result = 0;
while (pfd) {
if (const PerSpanData* childSpan = pfd->mSpan) {
return GetHangFrom(childSpan, aLineIsRTL);
}
if (pfd->mIsTextFrame) {
const auto* lastText = static_cast<const nsTextFrame*>(pfd->mFrame);
result = lastText->GetHangableISize();
// If the hangable space will be at the start edge of the line, due to
// its bidi direction being against the line direction, we flag this by
// negating the advance.
if (lastText->GetTextRun(nsTextFrame::eInflated)->IsRightToLeft() !=
aLineIsRTL) {
result = -result;
}
return result;
}
if (!pfd->mSkipWhenTrimmingWhitespace) {
// If we hit a frame on the end that's not text and not a placeholder or
// <br>, then there is no trailing whitespace to hang. Stop the search.
return result;
}
// Scan back for a preceding frame whose whitespace we can hang.
pfd = pfd->mPrev;
}
return result;
}
// Align inline frames within the line according to the CSS text-align
// property.
void nsLineLayout::TextAlignLine(nsLineBox* aLine, bool aIsLastLine) {
@ -3049,8 +3079,14 @@ void nsLineLayout::TextAlignLine(nsLineBox* aLine, bool aIsLastLine) {
StyleTextAlign textAlign =
aIsLastLine ? mStyleText->TextAlignForLastLine() : mStyleText->mTextAlign;
// Check if there's trailing whitespace we need to "hang" at line-wrap.
nscoord hang = 0;
if (aLine->IsLineWrapped()) {
hang = GetHangFrom(mRootSpan, lineWM.IsBidiRTL());
}
bool isSVG = LineContainerFrame()->IsInSVGTextSubtree();
bool doTextAlign = remainingISize > 0;
bool doTextAlign = remainingISize > 0 || hang != 0;
int32_t additionalGaps = 0;
if (!isSVG &&
@ -3107,31 +3143,40 @@ void nsLineLayout::TextAlignLine(nsLineBox* aLine, bool aIsLastLine) {
case StyleTextAlign::Start:
case StyleTextAlign::Char:
// default alignment is to start edge so do nothing.
// Default alignment is to start edge so do nothing, except to apply
// any "reverse-hang" amount resulting from reversed-direction trailing
// space.
// Char is for tables so treat as start if we find it in block layout.
if (hang < 0) {
dx = hang;
}
break;
case StyleTextAlign::Left:
case StyleTextAlign::MozLeft:
if (lineWM.IsBidiRTL()) {
dx = remainingISize;
dx = remainingISize + (hang > 0 ? hang : 0);
} else if (hang < 0) {
dx = hang;
}
break;
case StyleTextAlign::Right:
case StyleTextAlign::MozRight:
if (lineWM.IsBidiLTR()) {
dx = remainingISize;
dx = remainingISize + (hang > 0 ? hang : 0);
} else if (hang < 0) {
dx = hang;
}
break;
case StyleTextAlign::End:
dx = remainingISize;
dx = remainingISize + (hang > 0 ? hang : 0);
break;
case StyleTextAlign::Center:
case StyleTextAlign::MozCenter:
dx = remainingISize / 2;
dx = (remainingISize + hang) / 2;
break;
}
}

Просмотреть файл

@ -524,6 +524,10 @@ class nsLineLayout {
.GetPhysicalSize(mRootSpan->mWritingMode);
}
// Get the advance of any trailing hangable whitespace. If the whitespace
// has directionality opposite to the line, the result is negated.
nscoord GetHangFrom(const PerSpanData* aSpan, bool aLineIsRTL);
gfxBreakPriority mLastOptionalBreakPriority;
int32_t mLastOptionalBreakFrameOffset;
int32_t mForceBreakFrameOffset;

Просмотреть файл

@ -197,6 +197,8 @@ NS_DECLARE_FRAME_PROPERTY_RELEASABLE(UninflatedTextRunProperty, gfxTextRun)
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FontSizeInflationProperty, float)
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(HangableWhitespaceProperty, nscoord)
struct nsTextFrame::PaintTextSelectionParams : nsTextFrame::PaintTextParams {
Point textBaselinePt;
PropertyProvider* provider = nullptr;
@ -8148,6 +8150,25 @@ void nsTextFrame::SetFontSizeInflation(float aInflation) {
SetProperty(FontSizeInflationProperty(), aInflation);
}
void nsTextFrame::SetHangableISize(nscoord aISize) {
MOZ_ASSERT(aISize >= 0, "unexpected negative hangable advance");
if (aISize <= 0) {
if (mHasHangableWS) {
RemoveProperty(HangableWhitespaceProperty());
}
mHasHangableWS = false;
return;
}
SetProperty(HangableWhitespaceProperty(), aISize);
mHasHangableWS = true;
}
nscoord nsTextFrame::GetHangableISize() const {
MOZ_ASSERT(mHasHangableWS == HasProperty(HangableWhitespaceProperty()),
"flag/property mismatch!");
return mHasHangableWS ? GetProperty(HangableWhitespaceProperty()) : 0;
}
/* virtual */
void nsTextFrame::MarkIntrinsicISizesDirty() {
ClearTextRuns();
@ -9242,16 +9263,23 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
textMetrics.mAdvanceWidth -= trimmableWidth;
trimmableWidth = 0.0;
}
SetHangableISize(0);
} else if (whitespaceCanHang) {
// Figure out how much will hang.
// XXX This probably needs to be passed down and handled by nsLineLayout
// rather than here, e.g. for bug 1712703 and 1253840.
// Figure out how much whitespace will hang if at end-of-line.
gfxFloat hang =
std::min(std::max(0.0, textMetrics.mAdvanceWidth - availWidth),
trimmableWidth);
SetHangableISize(NSToCoordRound(trimmableWidth - hang));
textMetrics.mAdvanceWidth -= hang;
trimmableWidth = 0.0;
} else {
MOZ_ASSERT_UNREACHABLE("How did trimmableWidth get set?!");
SetHangableISize(0);
trimmableWidth = 0.0;
}
} else {
// Remove any stale frame property.
SetHangableISize(0);
}
if (!brokeText && lastBreak >= 0) {

Просмотреть файл

@ -771,6 +771,9 @@ class nsTextFrame : public nsIFrame {
// (To be used only on the first textframe in the chain.)
nsTextFrame* FindContinuationForOffset(int32_t aOffset);
void SetHangableISize(nscoord aISize);
nscoord GetHangableISize() const;
protected:
virtual ~nsTextFrame();
@ -808,6 +811,11 @@ class nsTextFrame : public nsIFrame {
// Whether a cached continuations array is present.
bool mHasContinuationsProperty = false;
// Whether a HangableWhitespace property is present. This could have been a
// frame state bit, but they are currently full. Because we have a uint8_t
// and a bool just above, there's a hole here that we can use.
bool mHasHangableWS = false;
/**
* Return true if the frame is part of a Selection.
* Helper method to implement the public IsSelected() API.