|
Lines 153-163
InlineDisplay::Line InlineDisplayLineBuilder::build(const LineBuilder::LineConte
a/Source/WebCore/layout/formattingContexts/inline/display/InlineDisplayLineBuilder.cpp_sec1
|
| 153 |
}; |
153 |
}; |
| 154 |
} |
154 |
} |
| 155 |
|
155 |
|
|
|
156 |
static float truncateTextContentWithMismatchingDirection(InlineDisplay::Box& displayBox, float contentWidth, float availableWidthForContent, bool canFullyTruncate) |
| 157 |
{ |
| 158 |
// While truncation normally starts at the end of the content and progress backwards, with mismatching direction |
| 159 |
// we take a different approach and truncate content the other way around (i.e. ellipsis follows inline direction truncating the beginning of the content). |
| 160 |
// <div dir=rtl>some long content</div> |
| 161 |
// [...ng content] |
| 162 |
auto& inlineTextBox = downcast<InlineTextBox>(displayBox.layoutBox()); |
| 163 |
auto& textContent = displayBox.text(); |
| 164 |
|
| 165 |
auto availableWidthForTruncatedContent = contentWidth - availableWidthForContent; |
| 166 |
auto truncatedSide = TextUtil::breakWord(inlineTextBox, textContent.start(), textContent.length(), contentWidth, availableWidthForTruncatedContent, { }, displayBox.style().fontCascade()); |
| 167 |
|
| 168 |
ASSERT(truncatedSide.length < textContent.length()); |
| 169 |
auto visibleLength = textContent.length() - truncatedSide.length; |
| 170 |
auto visibleWidth = contentWidth - truncatedSide.logicalWidth; |
| 171 |
// TextUtil::breakWord never returns overflowing content (left side) which means the right side is normally wider (by one character) than what we need. |
| 172 |
auto visibleContentOverflows = truncatedSide.logicalWidth < availableWidthForTruncatedContent; |
| 173 |
if (!visibleContentOverflows) { |
| 174 |
textContent.setPartiallyVisibleContentLength(visibleLength); |
| 175 |
return visibleWidth; |
| 176 |
} |
| 177 |
|
| 178 |
auto wouldFullyTruncate = visibleLength <= 1; |
| 179 |
if (wouldFullyTruncate && canFullyTruncate) { |
| 180 |
displayBox.setIsFullyTruncated(); |
| 181 |
return { }; |
| 182 |
} |
| 183 |
|
| 184 |
visibleLength = !wouldFullyTruncate ? visibleLength - 1 : 1; |
| 185 |
textContent.setPartiallyVisibleContentLength(visibleLength); |
| 186 |
auto visibleStartPosition = textContent.end() - visibleLength; |
| 187 |
return TextUtil::width(inlineTextBox, displayBox.style().fontCascade(), visibleStartPosition, textContent.end(), { }, TextUtil::UseTrailingWhitespaceMeasuringOptimization::No); |
| 188 |
} |
| 189 |
|
| 190 |
static float truncate(InlineDisplay::Box& displayBox, float contentWidth, float availableWidthForContent, bool canFullyTruncate) |
| 191 |
{ |
| 192 |
if (displayBox.isText()) { |
| 193 |
if (!availableWidthForContent && canFullyTruncate) { |
| 194 |
displayBox.setIsFullyTruncated(); |
| 195 |
return { }; |
| 196 |
} |
| 197 |
auto contentDirection = displayBox.bidiLevel() % 2 ? TextDirection::RTL : TextDirection::LTR; |
| 198 |
if (displayBox.layoutBox().parent().style().direction() != contentDirection) |
| 199 |
return truncateTextContentWithMismatchingDirection(displayBox, contentWidth, availableWidthForContent, canFullyTruncate); |
| 200 |
|
| 201 |
auto& inlineTextBox = downcast<InlineTextBox>(displayBox.layoutBox()); |
| 202 |
auto& textContent = displayBox.text(); |
| 203 |
auto visibleSide = TextUtil::breakWord(inlineTextBox, textContent.start(), textContent.length(), contentWidth, availableWidthForContent, { }, displayBox.style().fontCascade()); |
| 204 |
if (visibleSide.length) { |
| 205 |
textContent.setPartiallyVisibleContentLength(visibleSide.length); |
| 206 |
return visibleSide.logicalWidth; |
| 207 |
} |
| 208 |
if (canFullyTruncate) { |
| 209 |
displayBox.setIsFullyTruncated(); |
| 210 |
return { }; |
| 211 |
} |
| 212 |
auto firstCharacterLength = TextUtil::firstUserPerceivedCharacterLength(inlineTextBox, textContent.start(), textContent.length()); |
| 213 |
auto firstCharacterWidth = TextUtil::width(inlineTextBox, displayBox.style().fontCascade(), textContent.start(), textContent.start() + firstCharacterLength, { }, TextUtil::UseTrailingWhitespaceMeasuringOptimization::No); |
| 214 |
textContent.setPartiallyVisibleContentLength(firstCharacterLength); |
| 215 |
return firstCharacterWidth; |
| 216 |
} |
| 217 |
|
| 218 |
if (canFullyTruncate) { |
| 219 |
// Atomic inline level boxes are never partially truncated. |
| 220 |
displayBox.setIsFullyTruncated(); |
| 221 |
return { }; |
| 222 |
} |
| 223 |
return contentWidth; |
| 224 |
} |
| 225 |
|
| 156 |
static float truncateOverflowingDisplayBoxes(const InlineDisplay::Line& displayLine, InlineDisplay::Boxes& boxes, float ellipsisWidth, const RenderStyle& rootStyle) |
226 |
static float truncateOverflowingDisplayBoxes(const InlineDisplay::Line& displayLine, InlineDisplay::Boxes& boxes, float ellipsisWidth, const RenderStyle& rootStyle) |
| 157 |
{ |
227 |
{ |
| 158 |
// We gotta truncate some runs. |
228 |
// We gotta truncate some runs. |
| 159 |
ASSERT(displayLine.lineBoxLogicalRect().x() + displayLine.contentLogicalLeft() + displayLine.contentLogicalWidth() + ellipsisWidth > displayLine.lineBoxLogicalRect().maxX()); |
229 |
ASSERT(displayLine.lineBoxLogicalRect().x() + displayLine.contentLogicalLeft() + displayLine.contentLogicalWidth() + ellipsisWidth > displayLine.lineBoxLogicalRect().maxX()); |
| 160 |
auto isLeftToRightDirection = rootStyle.isLeftToRightDirection(); |
|
|
| 161 |
auto isHorizontal = rootStyle.isHorizontalWritingMode(); |
230 |
auto isHorizontal = rootStyle.isHorizontalWritingMode(); |
| 162 |
auto left = [&] (auto& displayBox) { |
231 |
auto left = [&] (auto& displayBox) { |
| 163 |
return isHorizontal ? displayBox.left() : displayBox.top(); |
232 |
return isHorizontal ? displayBox.left() : displayBox.top(); |
|
Lines 168-214
static float truncateOverflowingDisplayBoxes(const InlineDisplay::Line& displayL
a/Source/WebCore/layout/formattingContexts/inline/display/InlineDisplayLineBuilder.cpp_sec2
|
| 168 |
auto width = [&] (auto& displayBox) { |
237 |
auto width = [&] (auto& displayBox) { |
| 169 |
return isHorizontal ? displayBox.width() : displayBox.height(); |
238 |
return isHorizontal ? displayBox.width() : displayBox.height(); |
| 170 |
}; |
239 |
}; |
| 171 |
auto truncate = [&] (auto& displayBox, auto visibleWidth, auto canFullyTruncate) { |
|
|
| 172 |
if (displayBox.isText()) { |
| 173 |
if (!visibleWidth && canFullyTruncate) { |
| 174 |
displayBox.setIsFullyTruncated(); |
| 175 |
return isLeftToRightDirection ? left(displayBox) : right(displayBox); |
| 176 |
} |
| 177 |
|
| 178 |
auto& inlineTextBox = downcast<InlineTextBox>(displayBox.layoutBox()); |
| 179 |
auto& textContent = displayBox.text(); |
| 180 |
auto leftSide = TextUtil::breakWord(inlineTextBox, textContent.start(), textContent.length(), width(displayBox), visibleWidth, { }, displayBox.style().fontCascade()); |
| 181 |
if (leftSide.length) { |
| 182 |
textContent.setPartiallyVisibleContentLength(leftSide.length); |
| 183 |
return isLeftToRightDirection ? left(displayBox) + leftSide.logicalWidth : right(displayBox) - leftSide.logicalWidth; |
| 184 |
} |
| 185 |
if (canFullyTruncate) { |
| 186 |
displayBox.setIsFullyTruncated(); |
| 187 |
return isLeftToRightDirection ? left(displayBox) : right(displayBox); |
| 188 |
} |
| 189 |
auto firstCharacterLength = TextUtil::firstUserPerceivedCharacterLength(inlineTextBox, textContent.start(), textContent.length()); |
| 190 |
auto firstCharacterWidth = TextUtil::width(inlineTextBox, displayBox.style().fontCascade(), textContent.start(), textContent.start() + firstCharacterLength, { }, TextUtil::UseTrailingWhitespaceMeasuringOptimization::No); |
| 191 |
textContent.setPartiallyVisibleContentLength(firstCharacterLength); |
| 192 |
return isLeftToRightDirection ? left(displayBox) + firstCharacterWidth : right(displayBox) - firstCharacterWidth; |
| 193 |
} |
| 194 |
if (canFullyTruncate) { |
| 195 |
// Atomic inline level boxes are never partially truncated. |
| 196 |
displayBox.setIsFullyTruncated(); |
| 197 |
return isLeftToRightDirection ? left(displayBox) : right(displayBox); |
| 198 |
} |
| 199 |
return isLeftToRightDirection ? right(displayBox) : left(displayBox); |
| 200 |
}; |
| 201 |
|
| 202 |
// The logically first character or atomic inline-level element on a line must be clipped rather than ellipsed. |
240 |
// The logically first character or atomic inline-level element on a line must be clipped rather than ellipsed. |
| 203 |
auto isFirstContentRun = true; |
241 |
auto isFirstContentRun = true; |
| 204 |
if (isLeftToRightDirection) { |
242 |
if (rootStyle.isLeftToRightDirection()) { |
| 205 |
auto visualRightForContentEnd = std::max(0.f, right(displayLine) - ellipsisWidth); |
243 |
auto visualRightForContentEnd = std::max(0.f, right(displayLine) - ellipsisWidth); |
| 206 |
auto truncateRight = std::optional<float> { }; |
244 |
auto truncateRight = std::optional<float> { }; |
| 207 |
for (auto& displayBox : boxes) { |
245 |
for (auto& displayBox : boxes) { |
| 208 |
if (displayBox.isInlineBox()) |
246 |
if (displayBox.isInlineBox()) |
| 209 |
continue; |
247 |
continue; |
| 210 |
if (right(displayBox) > visualRightForContentEnd) { |
248 |
if (right(displayBox) > visualRightForContentEnd) { |
| 211 |
auto truncatePosition = truncate(displayBox, std::max(0.f, visualRightForContentEnd - left(displayBox)), !isFirstContentRun); |
249 |
auto visibleWidth = truncate(displayBox, width(displayBox), std::max(0.f, visualRightForContentEnd - left(displayBox)), !isFirstContentRun); |
|
|
250 |
auto truncatePosition = left(displayBox) + visibleWidth; |
| 212 |
truncateRight = truncateRight.value_or(truncatePosition); |
251 |
truncateRight = truncateRight.value_or(truncatePosition); |
| 213 |
} |
252 |
} |
| 214 |
isFirstContentRun = false; |
253 |
isFirstContentRun = false; |
|
Lines 223-229
static float truncateOverflowingDisplayBoxes(const InlineDisplay::Line& displayL
a/Source/WebCore/layout/formattingContexts/inline/display/InlineDisplayLineBuilder.cpp_sec3
|
| 223 |
if (displayBox.isInlineBox()) |
262 |
if (displayBox.isInlineBox()) |
| 224 |
continue; |
263 |
continue; |
| 225 |
if (left(displayBox) < visualLeftForContentEnd) { |
264 |
if (left(displayBox) < visualLeftForContentEnd) { |
| 226 |
auto truncatePosition = truncate(displayBox, std::max(0.f, right(displayBox) - visualLeftForContentEnd), !isFirstContentRun); |
265 |
auto visibleWidth = truncate(displayBox, width(displayBox), std::max(0.f, right(displayBox) - visualLeftForContentEnd), !isFirstContentRun); |
|
|
266 |
auto truncatePosition = right(displayBox) - visibleWidth; |
| 227 |
truncateLeft = truncateLeft.value_or(truncatePosition); |
267 |
truncateLeft = truncateLeft.value_or(truncatePosition); |
| 228 |
} |
268 |
} |
| 229 |
isFirstContentRun = false; |
269 |
isFirstContentRun = false; |