Initial work on text truncation. Working top-down, I think I've made all the change...
authorhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Jun 2004 07:21:23 +0000 (07:21 +0000)
committerhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Jun 2004 07:21:23 +0000 (07:21 +0000)
and to the block code itself.  The rest of the code can be concentrated in the render_line and render_text files.

        Reviewed by darin

        * khtml/rendering/bidi.cpp:
        (khtml::RenderBlock::layoutInlineChildren):
        (khtml::RenderBlock::findNextLineBreak):
        (khtml::RenderBlock::deleteEllipsisLineBoxes):
        (khtml::RenderBlock::checkLinesForTextOverflow):
        * khtml/rendering/render_block.h:
        * khtml/rendering/render_line.cpp:
        (RootInlineBox::detach):
        (RootInlineBox::detachEllipsisBox):
        (RootInlineBox::canAccommodateEllipsis):
        (RootInlineBox::placeEllipsis):
        * khtml/rendering/render_line.h:
        (khtml::RootInlineBox::RootInlineBox):
        * khtml/rendering/render_text.cpp:
        (RenderText::clearTextOverflowTruncation):
        * khtml/rendering/render_text.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@6843 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog-2005-08-23
WebCore/khtml/rendering/bidi.cpp
WebCore/khtml/rendering/render_block.h
WebCore/khtml/rendering/render_line.cpp
WebCore/khtml/rendering/render_line.h
WebCore/khtml/rendering/render_text.cpp
WebCore/khtml/rendering/render_text.h

index 577eeeb..cd8633b 100644 (file)
@@ -1,3 +1,27 @@
+2004-06-15  David Hyatt  <hyatt@apple.com>
+
+       Initial work on text truncation.  Working top-down, I think I've made all the changes I will need to bidi.cpp
+       and to the block code itself.  The rest of the code can be concentrated in the render_line and render_text files.
+       
+        Reviewed by darin
+
+        * khtml/rendering/bidi.cpp:
+        (khtml::RenderBlock::layoutInlineChildren):
+        (khtml::RenderBlock::findNextLineBreak):
+        (khtml::RenderBlock::deleteEllipsisLineBoxes):
+        (khtml::RenderBlock::checkLinesForTextOverflow):
+        * khtml/rendering/render_block.h:
+        * khtml/rendering/render_line.cpp:
+        (RootInlineBox::detach):
+        (RootInlineBox::detachEllipsisBox):
+        (RootInlineBox::canAccommodateEllipsis):
+        (RootInlineBox::placeEllipsis):
+        * khtml/rendering/render_line.h:
+        (khtml::RootInlineBox::RootInlineBox):
+        * khtml/rendering/render_text.cpp:
+        (RenderText::clearTextOverflowTruncation):
+        * khtml/rendering/render_text.h:
+
 2004-06-14  Trey Matteson  <trey@apple.com>
 
        3692690 - REGRESSION: canceling drag from WebView cause link to load
index 13e0534..289e837 100644 (file)
@@ -1333,7 +1333,18 @@ QRect RenderBlock::layoutInlineChildren(bool relayoutChildren)
     bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren || containsFloats();
     if (fullLayout)
         deleteLineBoxes();
+        
+    // Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't
+    // clip.
+    // FIXME: CSS3 says that descendants that are clipped must also know how to truncate.  This is insanely
+    // difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense
+    // anyway, so we won't worry about following the draft here.
+    bool hasTextOverflow = style()->textOverflow() && hasOverflowClip();
     
+    // Walk all the lines and delete our ellipsis line boxes if they exist.
+    if (hasTextOverflow)
+         deleteEllipsisLineBoxes();
+
     if (firstChild()) {
         // layout replaced elements
         bool endOfInline = false;
@@ -1356,6 +1367,8 @@ QRect RenderBlock::layoutInlineChildren(bool relayoutChildren)
             else if (o->isText() || (o->isInlineFlow() && !endOfInline)) {
                 if (fullLayout || o->selfNeedsLayout())
                     o->dirtyLineBoxes(fullLayout);
+                if (hasTextOverflow && isText())
+                    static_cast<RenderText*>(o)->clearTextOverflowTruncation();
                 o->setNeedsLayout(false);
             }
             o = Bidinext( this, o, bidi, false, &endOfInline);
@@ -1557,7 +1570,12 @@ QRect RenderBlock::layoutInlineChildren(bool relayoutChildren)
     if (!firstLineBox() && element() && element()->isContentEditable() && element()->rootEditableElement() == element()) {
         m_height += lineHeight(true);
     }
-    
+
+    // See if we have any lines that spill out of our block.  If we do, then we will possibly need to
+    // truncate text.
+    if (hasTextOverflow)
+        checkLinesForTextOverflow();
+
     return repaintRect;
 
 #if BIDI_DEBUG > 1
@@ -2230,6 +2248,45 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi
     return lBreak;
 }
 
+void RenderBlock::deleteEllipsisLineBoxes()
+{
+    RenderArena* arena = renderArena();
+    for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox())
+        curr->detachEllipsisBox(arena);
+}
+
+void RenderBlock::checkLinesForTextOverflow()
+{
+    // Determine the width of the ellipsis using the current font.
+    QChar ellipsis = 0x2026; // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if 0x2026 not renderable
+    const Font& firstLineFont = style(true)->htmlFont();
+    const Font& font = style()->htmlFont();
+    int firstLineEllipsisWidth = firstLineFont.width(&ellipsis, 1, 0);
+    int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(&ellipsis, 1, 0);
+
+    // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
+    // if the right edge of a line box exceeds that.  For RTL, we use the left edge of the padding box and
+    // check the left edge of the line box to see if it is less
+    // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
+    bool ltr = style()->direction() == LTR;
+    int blockEdge = ltr ? 
+                    borderLeft() + paddingLeft() + contentWidth() :
+                    borderLeft() + paddingLeft(); // FIXME: In theory we will one day do RTL scrollbars, and then this
+                                                  // RTL computation will want to add in the vertical scroller's width.
+    for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
+        int lineBoxEdge = ltr ? curr->xPos() : curr->xPos() + curr->width();
+        if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) {
+            // This line spills out of our box in the appopriate direction.  Now we need to see if the line
+            // can be truncated.  In order for truncation to be possible, the line must have sufficient space to
+            // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
+            // space.
+            int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth;
+            if (curr->canAccommodateEllipsis(blockEdge, ltr, width))
+                curr->placeEllipsis(&ellipsis, blockEdge, ltr, width);
+        }
+    }
+}
+
 // For --enable-final
 #undef BIDI_DEBUG
 #undef DEBUG_LINEBREAKS
index 92f274a..64c1e6b 100644 (file)
@@ -126,6 +126,8 @@ public:
     InlineFlowBox* createLineBoxes(RenderObject* obj);
     void computeHorizontalPositionsForLine(RootInlineBox* lineBox, BidiState &bidi);
     void computeVerticalPositionsForLine(RootInlineBox* lineBox);
+    void deleteEllipsisLineBoxes();
+    void checkLinesForTextOverflow();
     // end bidi.cpp functions
     
     virtual void paint(PaintInfo& i, int tx, int ty);
index 4f92cc5..82d9f91 100644 (file)
@@ -800,6 +800,42 @@ InlineBox* InlineBox::closestLeafChildForXPos(int _x, int _tx)
     return box->closestLeafChildForXPos(_x, _tx);
 }
 
+void RootInlineBox::detach(RenderArena* arena)
+{
+    detachEllipsisBox(arena);
+    InlineFlowBox::detach(arena);
+}
+
+void RootInlineBox::detachEllipsisBox(RenderArena* arena)
+{
+    if (m_ellipsisBox) {
+        m_ellipsisBox->detach(arena);
+        m_ellipsisBox = 0;
+    }
+}
+
+bool RootInlineBox::canAccommodateEllipsis(int blockEdge, bool ltr, int ellipsisWidth)
+{
+    // First sanity-check the width of the whole line to see if there is sufficient room.
+    if (width() < ellipsisWidth)
+        return false;
+        
+    // Next iterate over all the line boxes on the line.  If we find a replaced element that intersects
+    // then we refuse to accommodate the ellipsis.  Otherwise we're ok.
+    return false;
+}
+
+void RootInlineBox::placeEllipsis(QChar* ellipsisStr, int blockEdge, bool ltr, int ellipsisWidth)
+{
+    // Create an ellipsis box.
+    
+    // Position the ellipsis box vertically.
+    
+    // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL)
+    // of that glyph.  Mark all of the objects that intersect the ellipsis box as not painting (as being
+    // truncated).
+}
+
 void RootInlineBox::adjustVerticalPosition(int delta)
 {
     InlineFlowBox::adjustVerticalPosition(delta);
index 900196a..1edfbd5 100644 (file)
@@ -45,7 +45,7 @@ public:
 
     virtual ~InlineBox() {};
 
-    void detach(RenderArena* renderArena);
+    virtual void detach(RenderArena* renderArena);
 
     virtual void deleteLine(RenderArena* arena);
     virtual void extractLine();
@@ -273,9 +273,12 @@ class RootInlineBox : public InlineFlowBox
 public:
     RootInlineBox(RenderObject* obj)
     : InlineFlowBox(obj), m_topOverflow(0), m_bottomOverflow(0), m_lineBreakObj(0), m_lineBreakPos(0), 
-      m_blockHeight(0), m_endsWithBreak(false)
+      m_blockHeight(0), m_endsWithBreak(false), m_ellipsisBox(0)
     {}
     
+    virtual void detach(RenderArena* renderArena);
+    void detachEllipsisBox(RenderArena* renderArena);
+
     RootInlineBox* nextRootBox() { return static_cast<RootInlineBox*>(m_nextLine); }
     RootInlineBox* prevRootBox() { return static_cast<RootInlineBox*>(m_prevLine); }
 
@@ -300,6 +303,9 @@ public:
 
     void childRemoved(InlineBox* box);
 
+    bool canAccommodateEllipsis(int blockEdge, bool ltr, int ellipsisWidth);
+    void placeEllipsis(QChar* ellipsisStr, int blockEdge, bool ltr, int ellipsisWidth);
+
 protected:
     // Normally we are only as tall as the style on our block dictates, but we might have content
     // that spills out above the height of our font (e.g, a tall image), or something that extends further
@@ -317,6 +323,9 @@ protected:
     
     // Whether the line ends with a <br>.
     bool m_endsWithBreak;
+    
+    // An inline text box that represents our text truncation string.
+    InlineBox* m_ellipsisBox;
 };
 
 }; //namespace
index 477b0a6..3eb2793 100644 (file)
@@ -568,14 +568,6 @@ void RenderText::posOfChar(int chr, int &x, int &y)
     }
 }
 
-int RenderText::rightmostPosition() const
-{
-    if (style()->whiteSpace() != NORMAL)
-        return maxWidth();
-
-    return 0;
-}
-
 static int
 simpleDifferenceBetweenColors(QColor c1, QColor c2)
 {
@@ -1470,6 +1462,10 @@ InlineBox *RenderText::inlineBox(long offset)
     return 0;
 }
 
+void RenderText::clearTextOverflowTruncation()
+{
+}
+   
 RenderTextFragment::RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str,
                                        int startOffset, int endOffset)
 :RenderText(_node, _str->substring(startOffset, endOffset)), 
index 37ae71c..169ce30 100644 (file)
@@ -203,8 +203,6 @@ public:
     virtual short marginLeft() const { return style()->marginLeft().minWidth(0); }
     virtual short marginRight() const { return style()->marginRight().minWidth(0); }
 
-    virtual int rightmostPosition() const;
-
     virtual QRect getAbsoluteRepaintRect();
 
     const QFontMetrics &metrics(bool firstLine) const;
@@ -218,6 +216,8 @@ public:
     
     virtual InlineBox *inlineBox(long offset);
     
+    void clearTextOverflowTruncation();
+
 #if APPLE_CHANGES
     int widthFromCache(const Font *, int start, int len) const;
     bool shouldUseMonospaceCache(const Font *) const;