зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
cf3daa649d
Коммит
489d96aeb1
|
@ -3024,6 +3024,38 @@ 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 (result) {
|
||||||
|
// 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
|
// Align inline frames within the line according to the CSS text-align
|
||||||
// property.
|
// property.
|
||||||
void nsLineLayout::TextAlignLine(nsLineBox* aLine, bool aIsLastLine) {
|
void nsLineLayout::TextAlignLine(nsLineBox* aLine, bool aIsLastLine) {
|
||||||
|
@ -3049,8 +3081,14 @@ void nsLineLayout::TextAlignLine(nsLineBox* aLine, bool aIsLastLine) {
|
||||||
StyleTextAlign textAlign =
|
StyleTextAlign textAlign =
|
||||||
aIsLastLine ? mStyleText->TextAlignForLastLine() : mStyleText->mTextAlign;
|
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 isSVG = LineContainerFrame()->IsInSVGTextSubtree();
|
||||||
bool doTextAlign = remainingISize > 0;
|
bool doTextAlign = remainingISize > 0 || hang != 0;
|
||||||
|
|
||||||
int32_t additionalGaps = 0;
|
int32_t additionalGaps = 0;
|
||||||
if (!isSVG &&
|
if (!isSVG &&
|
||||||
|
@ -3107,31 +3145,40 @@ void nsLineLayout::TextAlignLine(nsLineBox* aLine, bool aIsLastLine) {
|
||||||
|
|
||||||
case StyleTextAlign::Start:
|
case StyleTextAlign::Start:
|
||||||
case StyleTextAlign::Char:
|
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.
|
// Char is for tables so treat as start if we find it in block layout.
|
||||||
|
if (hang < 0) {
|
||||||
|
dx = hang;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StyleTextAlign::Left:
|
case StyleTextAlign::Left:
|
||||||
case StyleTextAlign::MozLeft:
|
case StyleTextAlign::MozLeft:
|
||||||
if (lineWM.IsBidiRTL()) {
|
if (lineWM.IsBidiRTL()) {
|
||||||
dx = remainingISize;
|
dx = remainingISize + (hang > 0 ? hang : 0);
|
||||||
|
} else if (hang < 0) {
|
||||||
|
dx = hang;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StyleTextAlign::Right:
|
case StyleTextAlign::Right:
|
||||||
case StyleTextAlign::MozRight:
|
case StyleTextAlign::MozRight:
|
||||||
if (lineWM.IsBidiLTR()) {
|
if (lineWM.IsBidiLTR()) {
|
||||||
dx = remainingISize;
|
dx = remainingISize + (hang > 0 ? hang : 0);
|
||||||
|
} else if (hang < 0) {
|
||||||
|
dx = hang;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StyleTextAlign::End:
|
case StyleTextAlign::End:
|
||||||
dx = remainingISize;
|
dx = remainingISize + (hang > 0 ? hang : 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StyleTextAlign::Center:
|
case StyleTextAlign::Center:
|
||||||
case StyleTextAlign::MozCenter:
|
case StyleTextAlign::MozCenter:
|
||||||
dx = remainingISize / 2;
|
dx = (remainingISize + hang) / 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -524,6 +524,10 @@ class nsLineLayout {
|
||||||
.GetPhysicalSize(mRootSpan->mWritingMode);
|
.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;
|
gfxBreakPriority mLastOptionalBreakPriority;
|
||||||
int32_t mLastOptionalBreakFrameOffset;
|
int32_t mLastOptionalBreakFrameOffset;
|
||||||
int32_t mForceBreakFrameOffset;
|
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(FontSizeInflationProperty, float)
|
||||||
|
|
||||||
|
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(HangableWhitespaceProperty, nscoord)
|
||||||
|
|
||||||
struct nsTextFrame::PaintTextSelectionParams : nsTextFrame::PaintTextParams {
|
struct nsTextFrame::PaintTextSelectionParams : nsTextFrame::PaintTextParams {
|
||||||
Point textBaselinePt;
|
Point textBaselinePt;
|
||||||
PropertyProvider* provider = nullptr;
|
PropertyProvider* provider = nullptr;
|
||||||
|
@ -8148,6 +8150,25 @@ void nsTextFrame::SetFontSizeInflation(float aInflation) {
|
||||||
SetProperty(FontSizeInflationProperty(), 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 */
|
/* virtual */
|
||||||
void nsTextFrame::MarkIntrinsicISizesDirty() {
|
void nsTextFrame::MarkIntrinsicISizesDirty() {
|
||||||
ClearTextRuns();
|
ClearTextRuns();
|
||||||
|
@ -9242,16 +9263,23 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
|
||||||
textMetrics.mAdvanceWidth -= trimmableWidth;
|
textMetrics.mAdvanceWidth -= trimmableWidth;
|
||||||
trimmableWidth = 0.0;
|
trimmableWidth = 0.0;
|
||||||
}
|
}
|
||||||
|
SetHangableISize(0);
|
||||||
} else if (whitespaceCanHang) {
|
} else if (whitespaceCanHang) {
|
||||||
// Figure out how much will hang.
|
// Figure out how much whitespace will hang if at end-of-line.
|
||||||
// XXX This probably needs to be passed down and handled by nsLineLayout
|
|
||||||
// rather than here, e.g. for bug 1712703 and 1253840.
|
|
||||||
gfxFloat hang =
|
gfxFloat hang =
|
||||||
std::min(std::max(0.0, textMetrics.mAdvanceWidth - availWidth),
|
std::min(std::max(0.0, textMetrics.mAdvanceWidth - availWidth),
|
||||||
trimmableWidth);
|
trimmableWidth);
|
||||||
|
SetHangableISize(NSToCoordRound(trimmableWidth - hang));
|
||||||
textMetrics.mAdvanceWidth -= hang;
|
textMetrics.mAdvanceWidth -= hang;
|
||||||
trimmableWidth = 0.0;
|
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) {
|
if (!brokeText && lastBreak >= 0) {
|
||||||
|
|
|
@ -771,6 +771,9 @@ class nsTextFrame : public nsIFrame {
|
||||||
// (To be used only on the first textframe in the chain.)
|
// (To be used only on the first textframe in the chain.)
|
||||||
nsTextFrame* FindContinuationForOffset(int32_t aOffset);
|
nsTextFrame* FindContinuationForOffset(int32_t aOffset);
|
||||||
|
|
||||||
|
void SetHangableISize(nscoord aISize);
|
||||||
|
nscoord GetHangableISize() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~nsTextFrame();
|
virtual ~nsTextFrame();
|
||||||
|
|
||||||
|
@ -808,6 +811,11 @@ class nsTextFrame : public nsIFrame {
|
||||||
// Whether a cached continuations array is present.
|
// Whether a cached continuations array is present.
|
||||||
bool mHasContinuationsProperty = false;
|
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.
|
* Return true if the frame is part of a Selection.
|
||||||
* Helper method to implement the public IsSelected() API.
|
* Helper method to implement the public IsSelected() API.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче