]> git.saurik.com Git - wxWidgets.git/commitdiff
Improve wxGrid cell wrapping in wxGridCellAutoWrapStringRenderer.
authorVadim Zeitlin <vadim@wxwidgets.org>
Sun, 16 Jun 2013 10:50:54 +0000 (10:50 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sun, 16 Jun 2013 10:50:54 +0000 (10:50 +0000)
Wrap the words too long to be shown on one line on several lines.

Also take the line breaks and TABs into account.

Closes #15249.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74245 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
include/wx/generic/gridctrl.h
samples/grid/griddemo.cpp
src/generic/gridctrl.cpp

index 4cd8f39acb426df1f119bdfeaf7dddd7e3147b21..5050dd61540d1a76f05a49d538bdb2a4bebe89d9 100644 (file)
@@ -671,6 +671,7 @@ All (GUI):
 - Fix wxStyledTextCtrl::SetInsertionPointEnd() (troelsk).
 - Add wxFileDialog::GetCurrentlySelectedFilename() (Carl Godkin).
 - Add wxMouseEvent::GetColumnsPerAction() (toiffel).
+- Improve wrapping of cell contents in wxGrid (nmset).
 
 wxGTK:
 
index b9ee0a1a0f027107067f6c207e2172d0f0f3ba54..d1e82b40ebc71393dd5fc941d98e54e3e3c9c970 100644 (file)
@@ -249,6 +249,28 @@ private:
                                 const wxRect& rect,
                                 int row, int col);
 
+    // Helper methods of GetTextLines()
+
+    // Break a single logical line of text into several physical lines, all of
+    // which are added to the lines array. The lines are broken at maxWidth and
+    // the dc is used for measuring text extent only.
+    void BreakLine(wxDC& dc,
+                   const wxString& logicalLine,
+                   wxCoord maxWidth,
+                   wxArrayString& lines);
+
+    // Break a word, which is supposed to be wider than maxWidth, into several
+    // lines, which are added to lines array and the last, incomplete, of which
+    // is returned in line output parameter.
+    //
+    // Returns the width of the last line.
+    wxCoord BreakWord(wxDC& dc,
+                      const wxString& word,
+                      wxCoord maxWidth,
+                      wxArrayString& lines,
+                      wxString& line);
+
+
 };
 
 #endif  // wxUSE_GRID
index 9674b3ef7932ced8912607356aab0c19cb5e0f6f..31e27af2406b28dbe79352e200fff7863d2f5fc2 100644 (file)
@@ -450,6 +450,21 @@ GridFrame::GridFrame()
 
     grid->SetCellValue( 0, 4, wxT("Can veto edit this cell") );
 
+    grid->SetColSize(10, 150);
+    wxString longtext = wxT("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\n\n");
+    longtext += wxT("With tabs :\n");
+    longtext += wxT("Home,\t\thome\t\t\tagain\n");
+    longtext += wxT("Long word at start :\n");
+    longtext += wxT("ILikeToBeHereWhen I can\n");
+    longtext += wxT("Long word in the middle :\n");
+    longtext += wxT("When IComeHome,ColdAnd tired\n");
+    longtext += wxT("Long last word :\n");
+    longtext += wxT("It's GoodToWarmMyBonesBesideTheFire");
+    grid->SetCellValue( 0, 10, longtext );
+    grid->SetCellRenderer(0 , 10, new wxGridCellAutoWrapStringRenderer);
+    grid->SetCellEditor( 0,  10 , new wxGridCellAutoWrapStringEditor);
+    grid->SetCellValue( 0, 11, wxT("K1 cell editor blocker") );
+
     grid->SetCellValue( 0, 5, wxT("Press\nCtrl+arrow\nto skip over\ncells") );
 
     grid->SetRowSize( 99, 60 );
index 65b7edd192acfe8b92a5f9b895ec99daabc1201a..734775d15cd2c6624313f2e3004fb713da2393a3 100644 (file)
@@ -295,58 +295,137 @@ wxGridCellAutoWrapStringRenderer::GetTextLines(wxGrid& grid,
                                                const wxRect& rect,
                                                int row, int col)
 {
-    wxString  data = grid.GetCellValue(row, col);
-
-    wxArrayString lines;
     dc.SetFont(attr.GetFont());
+    const wxCoord maxWidth = rect.GetWidth();
 
-    //Taken from wxGrid again!
-    wxCoord x = 0, y = 0, curr_x = 0;
-    wxCoord max_x = rect.GetWidth();
-
-    dc.SetFont(attr.GetFont());
-    wxStringTokenizer tk(data , wxT(" \n\t\r"));
-    wxString thisline = wxEmptyString;
+    // Transform logical lines into physical ones, wrapping the longer ones.
+    const wxArrayString
+        logicalLines = wxSplit(grid.GetCellValue(row, col), '\n', '\0');
 
-    while ( tk.HasMoreTokens() )
+    wxArrayString physicalLines;
+    for ( wxArrayString::const_iterator it = logicalLines.begin();
+          it != logicalLines.end();
+          ++it )
     {
-        wxString tok = tk.GetNextToken();
-        //FIXME: this causes us to print an extra unnecessary
-        //       space at the end of the line. But it
-        //       is invisible , simplifies the size calculation
-        //       and ensures tokens are separated in the display
-        tok += wxT(" ");
+        const wxString& line = *it;
+
+        if ( dc.GetTextExtent(line).x > maxWidth )
+        {
+            // Line does not fit, break it up.
+            BreakLine(dc, line, maxWidth, physicalLines);
+        }
+        else // The entire line fits as is
+        {
+            physicalLines.push_back(line);
+        }
+    }
 
-        dc.GetTextExtent(tok, &x, &y);
-        if ( curr_x + x > max_x)
+    return physicalLines;
+}
+
+void
+wxGridCellAutoWrapStringRenderer::BreakLine(wxDC& dc,
+                                            const wxString& logicalLine,
+                                            wxCoord maxWidth,
+                                            wxArrayString& lines)
+{
+    wxCoord lineWidth = 0;
+    wxString line;
+
+    // For each word
+    wxStringTokenizer wordTokenizer(logicalLine, wxS(" \t"), wxTOKEN_RET_DELIMS);
+    while ( wordTokenizer.HasMoreTokens() )
+    {
+        const wxString word = wordTokenizer.GetNextToken();
+        const wxCoord wordWidth = dc.GetTextExtent(word).x;
+        if ( lineWidth + wordWidth < maxWidth )
         {
-            if ( curr_x == 0 )
+            // Word fits, just add it to this line.
+            line += word;
+            lineWidth += wordWidth;
+        }
+        else
+        {
+            // Word does not fit, check whether the word is itself wider that
+            // available width
+            if ( wordWidth < maxWidth )
             {
-                // this means that a single token is wider than the maximal
-                // width -- still use it as is as we need to show at least the
-                // part of it which fits
-                lines.Add(tok);
+                // Word can fit in a new line, put it at the beginning
+                // of the new line.
+                lines.push_back(line);
+                line = word;
+                lineWidth = wordWidth;
             }
-            else
+            else // Word cannot fit in available width at all.
             {
-                lines.Add(thisline);
-                thisline = tok;
-                curr_x = x;
+                if ( !line.empty() )
+                {
+                    lines.push_back(line);
+                    line.clear();
+                    lineWidth = 0;
+                }
+
+                // Break it up in several lines.
+                lineWidth = BreakWord(dc, word, maxWidth, lines, line);
             }
         }
-        else
-        {
-            thisline+= tok;
-            curr_x += x;
-        }
     }
-    //Add last line
-    lines.Add( wxString(thisline) );
 
-    return lines;
+    if ( !line.empty() )
+        lines.push_back(line);
 }
 
 
+wxCoord
+wxGridCellAutoWrapStringRenderer::BreakWord(wxDC& dc,
+                                            const wxString& word,
+                                            wxCoord maxWidth,
+                                            wxArrayString& lines,
+                                            wxString& line)
+{
+    wxArrayInt widths;
+    dc.GetPartialTextExtents(word, widths);
+
+    // TODO: Use binary search to find the first element > maxWidth.
+    const unsigned count = widths.size();
+    unsigned n;
+    for ( n = 0; n < count; n++ )
+    {
+        if ( widths[n] > maxWidth )
+            break;
+    }
+
+    if ( n == 0 )
+    {
+        // This is a degenerate case: the first character of the word is
+        // already wider than the available space, so we just can't show it
+        // completely and have to put the first character in this line.
+        n = 1;
+    }
+
+    lines.push_back(word.substr(0, n));
+
+    // Check if the remainder of the string fits in one line.
+    //
+    // Unfortunately we can't use the existing partial text extents as the
+    // extent of the remainder may be different when it's rendered in a
+    // separate line instead of as part of the same one, so we have to
+    // recompute it.
+    const wxString rest = word.substr(n);
+    const wxCoord restWidth = dc.GetTextExtent(rest).x;
+    if ( restWidth <= maxWidth )
+    {
+        line = rest;
+        return restWidth;
+    }
+
+    // Break the rest of the word into lines.
+    //
+    // TODO: Perhaps avoid recursion? The code is simpler like this but using a
+    // loop in this function would probably be more efficient.
+    return BreakWord(dc, rest, maxWidth, lines, line);
+}
+
 wxSize
 wxGridCellAutoWrapStringRenderer::GetBestSize(wxGrid& grid,
                                               wxGridCellAttr& attr,