OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/paint/InlineTextBoxPainter.h" | 5 #include "core/paint/InlineTextBoxPainter.h" |
6 | 6 |
7 #include "core/editing/CompositionUnderline.h" | 7 #include "core/editing/CompositionUnderline.h" |
8 #include "core/editing/Editor.h" | 8 #include "core/editing/Editor.h" |
9 #include "core/editing/markers/DocumentMarkerController.h" | 9 #include "core/editing/markers/DocumentMarkerController.h" |
10 #include "core/frame/LocalFrame.h" | 10 #include "core/frame/LocalFrame.h" |
(...skipping 541 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
552 int selectionEnd = 0; | 552 int selectionEnd = 0; |
553 if (paintSelectedTextOnly || paintSelectedTextSeparately) | 553 if (paintSelectedTextOnly || paintSelectedTextSeparately) |
554 m_inlineTextBox.selectionStartEnd(selectionStart, selectionEnd); | 554 m_inlineTextBox.selectionStartEnd(selectionStart, selectionEnd); |
555 | 555 |
556 bool respectHyphen = | 556 bool respectHyphen = |
557 selectionEnd == static_cast<int>(m_inlineTextBox.len()) && | 557 selectionEnd == static_cast<int>(m_inlineTextBox.len()) && |
558 m_inlineTextBox.hasHyphen(); | 558 m_inlineTextBox.hasHyphen(); |
559 if (respectHyphen) | 559 if (respectHyphen) |
560 selectionEnd = textRun.length(); | 560 selectionEnd = textRun.length(); |
561 | 561 |
| 562 bool ltr = m_inlineTextBox.isLeftToRightDirection(); |
| 563 bool flowIsLTR = |
| 564 m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection(); |
562 if (m_inlineTextBox.truncation() != cNoTruncation) { | 565 if (m_inlineTextBox.truncation() != cNoTruncation) { |
| 566 // In a mixed-direction flow the ellipsis is at the start of the text |
| 567 // rather than at the end of it. |
563 selectionStart = | 568 selectionStart = |
564 std::min<int>(selectionStart, m_inlineTextBox.truncation()); | 569 ltr == flowIsLTR |
565 selectionEnd = std::min<int>(selectionEnd, m_inlineTextBox.truncation()); | 570 ? std::min<int>(selectionStart, m_inlineTextBox.truncation()) |
566 length = m_inlineTextBox.truncation(); | 571 : std::max<int>(selectionStart, m_inlineTextBox.truncation()); |
| 572 selectionEnd = |
| 573 ltr == flowIsLTR |
| 574 ? std::min<int>(selectionEnd, m_inlineTextBox.truncation()) |
| 575 : std::max<int>(selectionEnd, m_inlineTextBox.truncation()); |
| 576 length = ltr == flowIsLTR ? m_inlineTextBox.truncation() : textRun.length(); |
567 } | 577 } |
568 | 578 |
569 TextPainter textPainter(context, font, textRun, textOrigin, boxRect, | 579 TextPainter textPainter(context, font, textRun, textOrigin, boxRect, |
570 m_inlineTextBox.isHorizontal()); | 580 m_inlineTextBox.isHorizontal()); |
571 TextEmphasisPosition emphasisMarkPosition; | 581 TextEmphasisPosition emphasisMarkPosition; |
572 bool hasTextEmphasis = | 582 bool hasTextEmphasis = |
573 m_inlineTextBox.getEmphasisMarkPosition(styleToUse, emphasisMarkPosition); | 583 m_inlineTextBox.getEmphasisMarkPosition(styleToUse, emphasisMarkPosition); |
574 if (hasTextEmphasis) | 584 if (hasTextEmphasis) |
575 textPainter.setEmphasisMark(styleToUse.textEmphasisMarkString(), | 585 textPainter.setEmphasisMark(styleToUse.textEmphasisMarkString(), |
576 emphasisMarkPosition); | 586 emphasisMarkPosition); |
577 if (combinedText) | 587 if (combinedText) |
578 textPainter.setCombinedText(combinedText); | 588 textPainter.setCombinedText(combinedText); |
| 589 if (m_inlineTextBox.truncation() != cNoTruncation && ltr != flowIsLTR) |
| 590 textPainter.setEllipsisOffset(m_inlineTextBox.truncation()); |
579 | 591 |
580 if (!paintSelectedTextOnly) { | 592 if (!paintSelectedTextOnly) { |
581 int startOffset = 0; | 593 int startOffset = 0; |
582 int endOffset = length; | 594 int endOffset = length; |
| 595 // Where the text and its flow have opposite directions then our offset into |
| 596 // the text given by |truncation| is at the start of the part that will be |
| 597 // visible. |
| 598 if (m_inlineTextBox.truncation() != cNoTruncation && ltr != flowIsLTR) { |
| 599 startOffset = m_inlineTextBox.truncation(); |
| 600 endOffset = textRun.length(); |
| 601 } |
| 602 |
583 if (paintSelectedTextSeparately && selectionStart < selectionEnd) { | 603 if (paintSelectedTextSeparately && selectionStart < selectionEnd) { |
584 startOffset = selectionEnd; | 604 startOffset = selectionEnd; |
585 endOffset = selectionStart; | 605 endOffset = selectionStart; |
586 } | 606 } |
587 // Where the text and its flow have opposite directions then our offset into | |
588 // the text given by |truncation| is at the start of the part that will be | |
589 // visible. | |
590 if (m_inlineTextBox.truncation() != cNoTruncation && | |
591 m_inlineTextBox.getLineLayoutItem() | |
592 .containingBlock() | |
593 .style() | |
594 ->isLeftToRightDirection() != | |
595 m_inlineTextBox.isLeftToRightDirection()) { | |
596 startOffset = m_inlineTextBox.truncation(); | |
597 endOffset = textRun.length(); | |
598 } | |
599 | 607 |
600 // FIXME: This cache should probably ultimately be held somewhere else. | 608 // FIXME: This cache should probably ultimately be held somewhere else. |
601 // A hashmap is convenient to avoid a memory hit when the | 609 // A hashmap is convenient to avoid a memory hit when the |
602 // RuntimeEnabledFeature is off. | 610 // RuntimeEnabledFeature is off. |
603 bool textBlobIsCacheable = startOffset == 0 && endOffset == length; | 611 bool textBlobIsCacheable = startOffset == 0 && endOffset == length; |
604 TextBlobPtr* cachedTextBlob = 0; | 612 TextBlobPtr* cachedTextBlob = 0; |
605 if (textBlobIsCacheable) | 613 if (textBlobIsCacheable) |
606 cachedTextBlob = addToTextBlobCache(m_inlineTextBox); | 614 cachedTextBlob = addToTextBlobCache(m_inlineTextBox); |
607 textPainter.paint(startOffset, endOffset, length, textStyle, | 615 textPainter.paint(startOffset, endOffset, length, textStyle, |
608 cachedTextBlob); | 616 cachedTextBlob); |
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
938 c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); | 946 c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); |
939 | 947 |
940 // If the text is truncated, let the thing being painted in the truncation | 948 // If the text is truncated, let the thing being painted in the truncation |
941 // draw its own highlight. | 949 // draw its own highlight. |
942 unsigned start = m_inlineTextBox.start(); | 950 unsigned start = m_inlineTextBox.start(); |
943 int length = m_inlineTextBox.len(); | 951 int length = m_inlineTextBox.len(); |
944 bool ltr = m_inlineTextBox.isLeftToRightDirection(); | 952 bool ltr = m_inlineTextBox.isLeftToRightDirection(); |
945 bool flowIsLTR = | 953 bool flowIsLTR = |
946 m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection(); | 954 m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection(); |
947 if (m_inlineTextBox.truncation() != cNoTruncation) { | 955 if (m_inlineTextBox.truncation() != cNoTruncation) { |
948 start = ltr == flowIsLTR ? m_inlineTextBox.start() | 956 // In a mixed-direction flow the ellipsis is at the start of the text |
949 : m_inlineTextBox.truncation(); | 957 // so we need to start after it. Otherwise we just need to make sure |
950 length = ltr == flowIsLTR | 958 // the end of the text is where the ellipsis starts. |
951 ? m_inlineTextBox.truncation() | 959 if (ltr != flowIsLTR) |
952 : m_inlineTextBox.len() - m_inlineTextBox.truncation(); | 960 sPos = std::max<int>(sPos, m_inlineTextBox.truncation()); |
| 961 else |
| 962 length = m_inlineTextBox.truncation(); |
953 } | 963 } |
954 StringView string(m_inlineTextBox.getLineLayoutItem().text(), start, | 964 StringView string(m_inlineTextBox.getLineLayoutItem().text(), start, |
955 static_cast<unsigned>(length)); | 965 static_cast<unsigned>(length)); |
956 | 966 |
957 StringBuilder charactersWithHyphen; | 967 StringBuilder charactersWithHyphen; |
958 bool respectHyphen = ePos == length && m_inlineTextBox.hasHyphen(); | 968 bool respectHyphen = ePos == length && m_inlineTextBox.hasHyphen(); |
959 TextRun textRun = m_inlineTextBox.constructTextRun( | 969 TextRun textRun = m_inlineTextBox.constructTextRun( |
960 style, string, m_inlineTextBox.getLineLayoutItem().textLength() - | 970 style, string, m_inlineTextBox.getLineLayoutItem().textLength() - |
961 m_inlineTextBox.start(), | 971 m_inlineTextBox.start(), |
962 respectHyphen ? &charactersWithHyphen : 0); | 972 respectHyphen ? &charactersWithHyphen : 0); |
(...skipping 18 matching lines...) Expand all Loading... |
981 int deltaY = roundToInt( | 991 int deltaY = roundToInt( |
982 m_inlineTextBox.getLineLayoutItem().style()->isFlippedLinesWritingMode() | 992 m_inlineTextBox.getLineLayoutItem().style()->isFlippedLinesWritingMode() |
983 ? selectionBottom - m_inlineTextBox.logicalBottom() | 993 ? selectionBottom - m_inlineTextBox.logicalBottom() |
984 : m_inlineTextBox.logicalTop() - selectionTop); | 994 : m_inlineTextBox.logicalTop() - selectionTop); |
985 int selHeight = std::max(0, roundToInt(selectionBottom - selectionTop)); | 995 int selHeight = std::max(0, roundToInt(selectionBottom - selectionTop)); |
986 | 996 |
987 FloatPoint localOrigin(boxRect.x().toFloat(), | 997 FloatPoint localOrigin(boxRect.x().toFloat(), |
988 (boxRect.y() - deltaY).toFloat()); | 998 (boxRect.y() - deltaY).toFloat()); |
989 LayoutRect selectionRect = LayoutRect( | 999 LayoutRect selectionRect = LayoutRect( |
990 font.selectionRectForText(textRun, localOrigin, selHeight, sPos, ePos)); | 1000 font.selectionRectForText(textRun, localOrigin, selHeight, sPos, ePos)); |
991 if (m_inlineTextBox.hasWrappedSelectionNewline() | 1001 // For line breaks, just painting a selection where the line break itself |
992 // For line breaks, just painting a selection where the line break itself | 1002 // is rendered is sufficient. Don't select it if there's an ellipsis |
993 // is rendered is sufficient. | 1003 // there. |
994 && !m_inlineTextBox.isLineBreak()) | 1004 if (m_inlineTextBox.hasWrappedSelectionNewline() && |
| 1005 m_inlineTextBox.truncation() == cNoTruncation && |
| 1006 !m_inlineTextBox.isLineBreak()) |
995 expandToIncludeNewlineForSelection(selectionRect); | 1007 expandToIncludeNewlineForSelection(selectionRect); |
996 | 1008 |
997 // Line breaks report themselves as having zero width for layout purposes, | 1009 // Line breaks report themselves as having zero width for layout purposes, |
998 // and so will end up positioned at (0, 0), even though we paint their | 1010 // and so will end up positioned at (0, 0), even though we paint their |
999 // selection highlight with character width. For RTL then, we have to | 1011 // selection highlight with character width. For RTL then, we have to |
1000 // explicitly shift the selection rect over to paint in the right location. | 1012 // explicitly shift the selection rect over to paint in the right location. |
1001 if (!m_inlineTextBox.isLeftToRightDirection() && | 1013 if (!m_inlineTextBox.isLeftToRightDirection() && |
1002 m_inlineTextBox.isLineBreak()) | 1014 m_inlineTextBox.isLineBreak()) |
1003 selectionRect.move(-selectionRect.width(), LayoutUnit()); | 1015 selectionRect.move(-selectionRect.width(), LayoutUnit()); |
1004 if (!flowIsLTR && m_inlineTextBox.truncation() != cNoTruncation) | 1016 if (!flowIsLTR && !ltr && m_inlineTextBox.truncation() != cNoTruncation) |
1005 selectionRect.move(m_inlineTextBox.logicalWidth() - selectionRect.width(), | 1017 selectionRect.move(m_inlineTextBox.logicalWidth() - selectionRect.width(), |
1006 LayoutUnit()); | 1018 LayoutUnit()); |
1007 | 1019 |
1008 context.fillRect(FloatRect(selectionRect), c); | 1020 context.fillRect(FloatRect(selectionRect), c); |
1009 } | 1021 } |
1010 | 1022 |
1011 void InlineTextBoxPainter::expandToIncludeNewlineForSelection( | 1023 void InlineTextBoxPainter::expandToIncludeNewlineForSelection( |
1012 LayoutRect& rect) { | 1024 LayoutRect& rect) { |
1013 FloatRectOutsets outsets = FloatRectOutsets(); | 1025 FloatRectOutsets outsets = FloatRectOutsets(); |
1014 float spaceWidth = m_inlineTextBox.newlineSpaceWidth(); | 1026 float spaceWidth = m_inlineTextBox.newlineSpaceWidth(); |
(...skipping 16 matching lines...) Expand all Loading... |
1031 GraphicsContextStateSaver stateSaver(context); | 1043 GraphicsContextStateSaver stateSaver(context); |
1032 | 1044 |
1033 LayoutPoint localOrigin(boxOrigin); | 1045 LayoutPoint localOrigin(boxOrigin); |
1034 | 1046 |
1035 LayoutUnit width = m_inlineTextBox.logicalWidth(); | 1047 LayoutUnit width = m_inlineTextBox.logicalWidth(); |
1036 if (m_inlineTextBox.truncation() != cNoTruncation) { | 1048 if (m_inlineTextBox.truncation() != cNoTruncation) { |
1037 bool ltr = m_inlineTextBox.isLeftToRightDirection(); | 1049 bool ltr = m_inlineTextBox.isLeftToRightDirection(); |
1038 bool flowIsLTR = | 1050 bool flowIsLTR = |
1039 m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection(); | 1051 m_inlineTextBox.getLineLayoutItem().style()->isLeftToRightDirection(); |
1040 width = LayoutUnit(m_inlineTextBox.getLineLayoutItem().width( | 1052 width = LayoutUnit(m_inlineTextBox.getLineLayoutItem().width( |
1041 ltr == flowIsLTR ? m_inlineTextBox.start() | 1053 ltr == flowIsLTR |
1042 : m_inlineTextBox.truncation(), | 1054 ? m_inlineTextBox.start() |
| 1055 : m_inlineTextBox.start() + m_inlineTextBox.truncation(), |
1043 ltr == flowIsLTR ? m_inlineTextBox.truncation() | 1056 ltr == flowIsLTR ? m_inlineTextBox.truncation() |
1044 : m_inlineTextBox.len() - m_inlineTextBox.truncation(), | 1057 : m_inlineTextBox.len() - m_inlineTextBox.truncation(), |
1045 m_inlineTextBox.textPos(), | 1058 m_inlineTextBox.textPos(), |
1046 flowIsLTR ? TextDirection::Ltr : TextDirection::Rtl, | 1059 flowIsLTR ? TextDirection::Ltr : TextDirection::Rtl, |
1047 m_inlineTextBox.isFirstLineStyle())); | 1060 m_inlineTextBox.isFirstLineStyle())); |
1048 if (!flowIsLTR) | 1061 if (!flowIsLTR) |
1049 localOrigin.move(m_inlineTextBox.logicalWidth() - width, LayoutUnit()); | 1062 localOrigin.move(m_inlineTextBox.logicalWidth() - width, LayoutUnit()); |
1050 } | 1063 } |
1051 | 1064 |
1052 LayoutObject& textBoxLayoutObject = inlineLayoutObject(); | 1065 LayoutObject& textBoxLayoutObject = inlineLayoutObject(); |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1276 | 1289 |
1277 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), | 1290 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), |
1278 m_inlineTextBox.logicalHeight())); | 1291 m_inlineTextBox.logicalHeight())); |
1279 context.clip(FloatRect(boxRect)); | 1292 context.clip(FloatRect(boxRect)); |
1280 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), | 1293 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), |
1281 boxRect.height().toInt(), color, | 1294 boxRect.height().toInt(), color, |
1282 paintOffsets.first, paintOffsets.second); | 1295 paintOffsets.first, paintOffsets.second); |
1283 } | 1296 } |
1284 | 1297 |
1285 } // namespace blink | 1298 } // namespace blink |
OLD | NEW |