Improve wxGrid cell wrapping in wxGridCellAutoWrapStringRenderer.
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
This commit is contained in:
@@ -671,6 +671,7 @@ All (GUI):
|
|||||||
- Fix wxStyledTextCtrl::SetInsertionPointEnd() (troelsk).
|
- Fix wxStyledTextCtrl::SetInsertionPointEnd() (troelsk).
|
||||||
- Add wxFileDialog::GetCurrentlySelectedFilename() (Carl Godkin).
|
- Add wxFileDialog::GetCurrentlySelectedFilename() (Carl Godkin).
|
||||||
- Add wxMouseEvent::GetColumnsPerAction() (toiffel).
|
- Add wxMouseEvent::GetColumnsPerAction() (toiffel).
|
||||||
|
- Improve wrapping of cell contents in wxGrid (nmset).
|
||||||
|
|
||||||
wxGTK:
|
wxGTK:
|
||||||
|
|
||||||
|
@@ -249,6 +249,28 @@ private:
|
|||||||
const wxRect& rect,
|
const wxRect& rect,
|
||||||
int row, int col);
|
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
|
#endif // wxUSE_GRID
|
||||||
|
@@ -450,6 +450,21 @@ GridFrame::GridFrame()
|
|||||||
|
|
||||||
grid->SetCellValue( 0, 4, wxT("Can veto edit this cell") );
|
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->SetCellValue( 0, 5, wxT("Press\nCtrl+arrow\nto skip over\ncells") );
|
||||||
|
|
||||||
grid->SetRowSize( 99, 60 );
|
grid->SetRowSize( 99, 60 );
|
||||||
|
@@ -295,58 +295,137 @@ wxGridCellAutoWrapStringRenderer::GetTextLines(wxGrid& grid,
|
|||||||
const wxRect& rect,
|
const wxRect& rect,
|
||||||
int row, int col)
|
int row, int col)
|
||||||
{
|
{
|
||||||
wxString data = grid.GetCellValue(row, col);
|
|
||||||
|
|
||||||
wxArrayString lines;
|
|
||||||
dc.SetFont(attr.GetFont());
|
dc.SetFont(attr.GetFont());
|
||||||
|
const wxCoord maxWidth = rect.GetWidth();
|
||||||
|
|
||||||
//Taken from wxGrid again!
|
// Transform logical lines into physical ones, wrapping the longer ones.
|
||||||
wxCoord x = 0, y = 0, curr_x = 0;
|
const wxArrayString
|
||||||
wxCoord max_x = rect.GetWidth();
|
logicalLines = wxSplit(grid.GetCellValue(row, col), '\n', '\0');
|
||||||
|
|
||||||
dc.SetFont(attr.GetFont());
|
wxArrayString physicalLines;
|
||||||
wxStringTokenizer tk(data , wxT(" \n\t\r"));
|
for ( wxArrayString::const_iterator it = logicalLines.begin();
|
||||||
wxString thisline = wxEmptyString;
|
it != logicalLines.end();
|
||||||
|
++it )
|
||||||
while ( tk.HasMoreTokens() )
|
|
||||||
{
|
{
|
||||||
wxString tok = tk.GetNextToken();
|
const wxString& line = *it;
|
||||||
//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(" ");
|
|
||||||
|
|
||||||
dc.GetTextExtent(tok, &x, &y);
|
if ( dc.GetTextExtent(line).x > maxWidth )
|
||||||
if ( curr_x + x > max_x)
|
|
||||||
{
|
{
|
||||||
if ( curr_x == 0 )
|
// Line does not fit, break it up.
|
||||||
{
|
BreakLine(dc, line, maxWidth, physicalLines);
|
||||||
// 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
|
else // The entire line fits as is
|
||||||
// part of it which fits
|
{
|
||||||
lines.Add(tok);
|
physicalLines.push_back(line);
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
|
||||||
lines.Add(thisline);
|
return physicalLines;
|
||||||
thisline = tok;
|
}
|
||||||
curr_x = x;
|
|
||||||
}
|
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 )
|
||||||
|
{
|
||||||
|
// Word fits, just add it to this line.
|
||||||
|
line += word;
|
||||||
|
lineWidth += wordWidth;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
thisline+= tok;
|
// Word does not fit, check whether the word is itself wider that
|
||||||
curr_x += x;
|
// available width
|
||||||
|
if ( wordWidth < maxWidth )
|
||||||
|
{
|
||||||
|
// 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 // Word cannot fit in available width at all.
|
||||||
|
{
|
||||||
|
if ( !line.empty() )
|
||||||
|
{
|
||||||
|
lines.push_back(line);
|
||||||
|
line.clear();
|
||||||
|
lineWidth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break it up in several lines.
|
||||||
|
lineWidth = BreakWord(dc, word, maxWidth, lines, line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//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
|
wxSize
|
||||||
wxGridCellAutoWrapStringRenderer::GetBestSize(wxGrid& grid,
|
wxGridCellAutoWrapStringRenderer::GetBestSize(wxGrid& grid,
|
||||||
wxGridCellAttr& attr,
|
wxGridCellAttr& attr,
|
||||||
|
Reference in New Issue
Block a user