Merge branch 'grid-ellipsize'

Implement support for ellipsization in wxGrid.

See https://github.com/wxWidgets/wxWidgets/pull/1705
This commit is contained in:
Vadim Zeitlin
2020-01-15 14:23:29 +01:00
6 changed files with 364 additions and 61 deletions

View File

@@ -146,6 +146,8 @@ Changes in behaviour which may result in build errors
- wxIntProperty::DoValidation() and wxFloatProperty::DoValidation() are
no longer public since they are helpers intended for internal use only.
- wxGridCellAttr ctor taking wxGridCellAttr pointer is now explicit.
3.1.4: (released ????-??-??)
----------------------------

View File

@@ -408,6 +408,70 @@ public:
wxRect& rect) const wxOVERRIDE;
};
// ----------------------------------------------------------------------------
// Helper class used to define What should happen if the cell contents doesn't
// fit into its allotted space.
// ----------------------------------------------------------------------------
class wxGridFitMode
{
public:
// Default ctor creates an object not specifying any particular behaviour.
wxGridFitMode() : m_mode(Mode_Unset) {}
// Static methods allowing to create objects actually specifying behaviour.
static wxGridFitMode Clip() { return wxGridFitMode(Mode_Clip); }
static wxGridFitMode Overflow() { return wxGridFitMode(Mode_Overflow); }
static wxGridFitMode Ellipsize(wxEllipsizeMode ellipsize = wxELLIPSIZE_END)
{
// This cast works because the enum elements are the same, see below.
return wxGridFitMode(static_cast<Mode>(ellipsize));
}
// Accessors.
bool IsSpecified() const { return m_mode != Mode_Unset; }
bool IsClip() const { return m_mode == Mode_Clip; }
bool IsOverflow() const { return m_mode == Mode_Overflow; }
wxEllipsizeMode GetEllipsizeMode() const
{
switch ( m_mode )
{
case Mode_Unset:
case Mode_EllipsizeStart:
case Mode_EllipsizeMiddle:
case Mode_EllipsizeEnd:
return static_cast<wxEllipsizeMode>(m_mode);
case Mode_Overflow:
case Mode_Clip:
break;
}
return wxELLIPSIZE_NONE;
}
// This one is used in the implementation only.
static wxGridFitMode FromOverflowFlag(bool allow)
{ return allow ? Overflow() : Clip(); }
private:
enum Mode
{
// This is a hack to save space: the first 4 elements of this enum are
// the same as those of wxEllipsizeMode.
Mode_Unset = wxELLIPSIZE_NONE,
Mode_EllipsizeStart = wxELLIPSIZE_START,
Mode_EllipsizeMiddle = wxELLIPSIZE_MIDDLE,
Mode_EllipsizeEnd = wxELLIPSIZE_END,
Mode_Overflow,
Mode_Clip
};
explicit wxGridFitMode(Mode mode) : m_mode(mode) {}
Mode m_mode;
};
// ----------------------------------------------------------------------------
// wxGridCellAttr: this class can be used to alter the cells appearance in
@@ -428,16 +492,15 @@ public:
Merged
};
// ctors
wxGridCellAttr(wxGridCellAttr *attrDefault = NULL)
// default ctor
explicit wxGridCellAttr(wxGridCellAttr *attrDefault = NULL)
{
Init(attrDefault);
SetAlignment(wxALIGN_INVALID, wxALIGN_INVALID);
}
// VZ: considering the number of members wxGridCellAttr has now, this ctor
// seems to be pretty useless... may be we should just remove it?
// ctor setting the most common attributes
wxGridCellAttr(const wxColour& colText,
const wxColour& colBack,
const wxFont& font,
@@ -463,8 +526,9 @@ public:
m_vAlign = vAlign;
}
void SetSize(int num_rows, int num_cols);
void SetFitMode(wxGridFitMode fitMode) { m_fitMode = fitMode; }
void SetOverflow(bool allow = true)
{ m_overflow = allow ? Overflow : SingleCell; }
{ SetFitMode(wxGridFitMode::FromOverflowFlag(allow)); }
void SetReadOnly(bool isReadOnly = true)
{ m_isReadOnly = isReadOnly ? ReadOnly : ReadWrite; }
@@ -487,7 +551,7 @@ public:
bool HasRenderer() const { return m_renderer != NULL; }
bool HasEditor() const { return m_editor != NULL; }
bool HasReadWriteMode() const { return m_isReadOnly != Unset; }
bool HasOverflowMode() const { return m_overflow != UnsetOverflow; }
bool HasOverflowMode() const { return m_fitMode.IsSpecified(); }
bool HasSize() const { return m_sizeRows != 1 || m_sizeCols != 1; }
const wxColour& GetTextColour() const;
@@ -504,8 +568,8 @@ public:
void GetNonDefaultAlignment(int *hAlign, int *vAlign) const;
void GetSize(int *num_rows, int *num_cols) const;
bool GetOverflow() const
{ return m_overflow != SingleCell; }
wxGridFitMode GetFitMode() const;
bool GetOverflow() const { return GetFitMode().IsOverflow(); }
wxGridCellRenderer *GetRenderer(const wxGrid* grid, int row, int col) const;
wxGridCellEditor *GetEditor(const wxGrid* grid, int row, int col) const;
@@ -531,13 +595,6 @@ private:
ReadOnly
};
enum wxAttrOverflowMode
{
UnsetOverflow = -1,
Overflow,
SingleCell
};
// the common part of all ctors
void Init(wxGridCellAttr *attrDefault = NULL);
@@ -550,7 +607,7 @@ private:
int m_sizeRows,
m_sizeCols;
wxAttrOverflowMode m_overflow;
wxGridFitMode m_fitMode;
wxGridCellRenderer* m_renderer;
wxGridCellEditor* m_editor;
@@ -1103,6 +1160,13 @@ public:
int verticalAlignment = wxALIGN_TOP,
int textOrientation = wxHORIZONTAL ) const;
void DrawTextRectangle(wxDC& dc,
const wxString& text,
const wxRect& rect,
const wxGridCellAttr& attr,
int defaultHAlign = wxALIGN_INVALID,
int defaultVAlign = wxALIGN_INVALID);
// ------ grid render function for printing
//
void Render( wxDC& dc,
@@ -1437,8 +1501,14 @@ public:
wxFont GetCellFont( int row, int col ) const;
void GetDefaultCellAlignment( int *horiz, int *vert ) const;
void GetCellAlignment( int row, int col, int *horiz, int *vert ) const;
bool GetDefaultCellOverflow() const;
bool GetCellOverflow( int row, int col ) const;
wxGridFitMode GetDefaultCellFitMode() const;
wxGridFitMode GetCellFitMode(int row, int col) const;
bool GetDefaultCellOverflow() const
{ return GetDefaultCellFitMode().IsOverflow(); }
bool GetCellOverflow( int row, int col ) const
{ return GetCellFitMode(row, col).IsOverflow(); }
// this function returns 1 in num_rows and num_cols for normal cells,
// positive numbers for a cell spanning multiple columns/rows (as set with
@@ -1590,8 +1660,14 @@ public:
void SetCellFont( int row, int col, const wxFont& );
void SetDefaultCellAlignment( int horiz, int vert );
void SetCellAlignment( int row, int col, int horiz, int vert );
void SetDefaultCellOverflow( bool allow );
void SetCellOverflow( int row, int col, bool allow );
void SetDefaultCellFitMode(wxGridFitMode fitMode);
void SetCellFitMode(int row, int col, wxGridFitMode fitMode);
void SetDefaultCellOverflow( bool allow )
{ SetDefaultCellFitMode(wxGridFitMode::FromOverflowFlag(allow)); }
void SetCellOverflow( int row, int col, bool allow )
{ SetCellFitMode(row, col, wxGridFitMode::FromOverflowFlag(allow)); }
void SetCellSize( int row, int col, int num_rows, int num_cols );
// takes ownership of the pointer

View File

@@ -873,6 +873,98 @@ public:
/**
@class wxGridFitMode
Allows to specify the behaviour when the cell contents doesn't fit into its
allotted space.
Objects of this class are used with wxGridCellAttr::SetFitMode() and
wxGrid::SetDefaultCellFitMode() and wxGrid::SetCellFitMode() functions and
allow to specify what should happen if the cell contents doesn't fit into
the available space. The possibilities are:
- Overflow into the cell to the right if it is empty, or possibly several
cells, if the cell contents still doesn't fit after overflowing into the
immediately neighbouring cell.
- Clip the cell contents, discarding the part which doesn't fit.
- Ellipsize the cell contents, i.e. replace the non-fitting part with
ellipsis (@c ...), putting the ellipsis at the end by default, but possibly
at the beginning or in the middle.
The default behaviour is to overflow, use wxGrid::SetDefaultCellFitMode()
to change this, for example:
@code
grid->SetDefaultCellFitMode(wxGridFitMode::Clip());
@endcode
Objects of this class are created using static functions instead of
constructors for better readability and can't be changed after creating
them except by using the assignment operator.
@library{wxcore}
@category{grid}
@since 3.1.4
*/
class wxGridFitMode
{
public:
/**
Default constructor creates an object not specifying any behaviour.
This constructor is not very useful, use static methods Clip() and
Overflow() below to create objects of this class instead.
*/
wxGridFitMode();
/**
Pseudo-constructor for object specifying clipping behaviour.
*/
static wxGridFitMode Clip();
/**
Pseudo-constructor for object specifying overflow behaviour.
*/
static wxGridFitMode Overflow();
/**
Pseudo-constructor for object specifying ellipsize behaviour.
*/
static wxGridFitMode Ellipsize(wxEllipsizeMode ellipsize = wxELLIPSIZE_END);
/**
Return true if the object specifies some particular behaviour.
This method returns @false for default-constructed objects of this
type only.
*/
bool IsSpecified() const;
/**
Return true if the object specifies clipping behaviour.
This method returns @true only for the objects returned by Clip().
*/
bool IsClip() const;
/**
Return true if the object specifies overflow behaviour.
This method returns @true only for the objects returned by Overflow().
*/
bool IsOverflow() const;
/**
Return ellipsize mode, possibly @c wxELLIPSIZE_NONE.
For the objects constructed using Ellipsize(), the same ellipsization
mode as was passed to it is returned. For all the other objects,
::wxELLIPSIZE_NONE is.
*/
wxEllipsizeMode GetEllipsizeMode() const;
};
/**
@class wxGridCellAttr
@@ -912,7 +1004,8 @@ public:
/**
Default constructor.
*/
wxGridCellAttr(wxGridCellAttr* attrDefault = NULL);
explicit wxGridCellAttr(wxGridCellAttr* attrDefault = NULL);
/**
Constructor specifying some of the often used attributes.
*/
@@ -1087,7 +1180,29 @@ public:
void MergeWith(wxGridCellAttr *mergefrom);
void SetSize(int num_rows, int num_cols);
/**
Specifies the behaviour of the cell contents if it doesn't fit into the
available space.
@see wxGridFitMode
@since 3.1.4
*/
void SetFitMode(wxGridFitMode fitMode);
/**
Specifies if cells using this attribute should overflow or clip their
contents.
This is the same as calling SetFitMode() with either
wxGridFitMode::Overflow() or wxGridFitMode::Clip() argument depending
on whether @a allow is @true or @false.
Prefer using SetFitMode() directly instead in the new code.
*/
void SetOverflow(bool allow = true);
void SetKind(wxAttrKind kind);
bool HasReadWriteMode() const;
@@ -1095,7 +1210,26 @@ public:
bool HasSize() const;
void GetSize(int *num_rows, int *num_cols) const;
/**
Returns the fitting mode for the cells using this attribute.
The returned wxGridFitMode is always specified, i.e.
wxGridFitMode::IsSpecified() always returns @true. The default value,
if SetFitMode() hadn't been called before, is "overflow".
@since 3.1.4
*/
wxGridFitMode GetFitMode() const;
/**
Returns true if the cells using this attribute overflow into the
neighbouring cells.
Prefer using GetFitMode() in the new code.
*/
bool GetOverflow() const;
wxAttrKind GetKind();
@@ -3317,10 +3451,22 @@ public:
*/
void AutoSizeRows(bool setAsMin = true);
/**
Returns the cell fitting mode.
@see wxGridFitMode
@since 3.1.4
*/
wxGridFitMode GetCellFitMode(int row, int col) const;
/**
Returns @true if the cell value can overflow.
A cell can overflow if the next cell in the row is empty.
This is identical to calling GetCellFitMode() and using
wxGridFitMode::IsOverflow() on the returned value.
Prefer using GetCellFitMode() directly in the new code.
*/
bool GetCellOverflow(int row, int col) const;
@@ -3349,8 +3495,25 @@ public:
*/
bool IsColShown(int col) const;
/**
Returns the default cell fitting mode.
The default mode is "overflow", but can be modified using
SetDefaultCellFitMode().
@see wxGridFitMode
@since 3.1.4
*/
wxGridFitMode GetDefaultCellFitMode() const;
/**
Returns @true if the cells can overflow by default.
This is identical to calling GetDefaultCellFitMode() and using
wxGridFitMode::IsOverflow() on the returned value.
Prefer using GetDefaultCellFitMode() directly in the new code.
*/
bool GetDefaultCellOverflow() const;
@@ -3399,8 +3562,20 @@ public:
*/
bool IsRowShown(int row) const;
/**
Specifies the behaviour of the cell contents if it doesn't fit into the
available space.
@see wxGridFitMode
@since 3.1.4
*/
void SetCellFitMode(int row, int col, wxGridFitMode fitMode);
/**
Sets the overflow permission of the cell.
Prefer using SetCellFitMode() in the new code.
*/
void SetCellOverflow(int row, int col, bool allow);
@@ -3469,8 +3644,20 @@ public:
void ShowCol(int col);
/**
Specifies the default behaviour of the cell contents if it doesn't fit
into the available space.
@see wxGridFitMode
@since 3.1.4
*/
void SetDefaultCellFitMode(wxGridFitMode fitMode);
/**
Sets the default overflow permission of the cells.
Prefer using SetDefaultCellFitMode() in the new code.
*/
void SetDefaultCellOverflow( bool allow );

View File

@@ -501,6 +501,9 @@ GridFrame::GridFrame()
grid->SetCellValue( 99, 99, "Ctrl+End\nwill go to\nthis cell" );
grid->SetCellValue( 1, 0, "This default cell will overflow into neighboring cells, but not if you turn overflow off.");
grid->SetCellValue(2, 0, "This one always overflows");
grid->SetCellFitMode(2, 0, wxGridFitMode::Overflow());
grid->SetCellTextColour(1, 2, *wxRED);
grid->SetCellBackgroundColour(1, 2, *wxGREEN);
@@ -585,6 +588,12 @@ GridFrame::GridFrame()
grid->SetCellRenderer(14, 1, new wxGridCellDateRenderer("%Y-%m-%d"));
grid->SetCellEditor(14, 1, new wxGridCellDateEditor);
grid->SetCellValue(13, 3, "String using default ellipsization");
grid->SetCellFitMode(13, 3, wxGridFitMode::Ellipsize());
grid->SetCellValue(13, 4, "String ellipsized in the middle");
grid->SetCellFitMode(13, 4, wxGridFitMode::Ellipsize(wxELLIPSIZE_MIDDLE));
const wxString choices[] =
{
"Please select a choice",

View File

@@ -409,7 +409,6 @@ void wxGridCellAttr::Init(wxGridCellAttr *attrDefault)
m_attrkind = wxGridCellAttr::Cell;
m_sizeRows = m_sizeCols = 1;
m_overflow = UnsetOverflow;
SetDefAttr(attrDefault);
}
@@ -443,7 +442,7 @@ wxGridCellAttr *wxGridCellAttr::Clone() const
if ( IsReadOnly() )
attr->SetReadOnly();
attr->SetOverflow( m_overflow == Overflow );
attr->m_fitMode = m_fitMode;
attr->SetKind( m_attrkind );
return attr;
@@ -626,6 +625,23 @@ void wxGridCellAttr::GetSize( int *num_rows, int *num_cols ) const
*num_cols = m_sizeCols;
}
wxGridFitMode wxGridCellAttr::GetFitMode() const
{
if ( m_fitMode.IsSpecified() )
{
return m_fitMode;
}
else if (m_defGridAttr && m_defGridAttr != this)
{
return m_defGridAttr->GetFitMode();
}
else
{
wxFAIL_MSG(wxT("Missing default cell attribute"));
return wxGridFitMode();
}
}
// GetRenderer and GetEditor use a slightly different decision path about
// which attribute to use. If a non-default attr object has one then it is
// used, otherwise the default editor or renderer is fetched from the grid and
@@ -2383,6 +2399,7 @@ void wxGrid::Create()
m_defaultCellAttr->SetAlignment(wxALIGN_LEFT, wxALIGN_TOP);
m_defaultCellAttr->SetRenderer(new wxGridCellStringRenderer);
m_defaultCellAttr->SetEditor(new wxGridCellTextEditor);
m_defaultCellAttr->SetFitMode(wxGridFitMode::Overflow());
#if _USE_VISATTR
wxVisualAttributes gva = wxListBox::GetClassDefaultAttributes();
@@ -6805,6 +6822,28 @@ void wxGrid::DrawTextRectangle(wxDC& dc,
}
}
void wxGrid::DrawTextRectangle(wxDC& dc,
const wxString& text,
const wxRect& rect,
const wxGridCellAttr& attr,
int hAlign,
int vAlign)
{
attr.GetNonDefaultAlignment(&hAlign, &vAlign);
// This does nothing if there is no need to ellipsize.
const wxString& ellipsizedText = wxControl::Ellipsize
(
text,
dc,
attr.GetFitMode().GetEllipsizeMode(),
rect.GetWidth(),
wxELLIPSIZE_FLAGS_NONE
);
DrawTextRectangle(dc, ellipsizedText, rect, hAlign, vAlign);
}
// Split multi-line text up into an array of strings.
// Any existing contents of the string array are preserved.
//
@@ -8441,9 +8480,9 @@ void wxGrid::SetDefaultCellAlignment( int horiz, int vert )
m_defaultCellAttr->SetAlignment(horiz, vert);
}
void wxGrid::SetDefaultCellOverflow( bool allow )
void wxGrid::SetDefaultCellFitMode(wxGridFitMode fitMode)
{
m_defaultCellAttr->SetOverflow(allow);
m_defaultCellAttr->SetFitMode(fitMode);
}
void wxGrid::SetDefaultCellFont( const wxFont& font )
@@ -8494,9 +8533,9 @@ void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert ) const
m_defaultCellAttr->GetAlignment(horiz, vert);
}
bool wxGrid::GetDefaultCellOverflow() const
wxGridFitMode wxGrid::GetDefaultCellFitMode() const
{
return m_defaultCellAttr->GetOverflow();
return m_defaultCellAttr->GetFitMode();
}
wxGridCellRenderer *wxGrid::GetDefaultRenderer() const
@@ -8547,13 +8586,13 @@ void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert ) const
attr->DecRef();
}
bool wxGrid::GetCellOverflow( int row, int col ) const
wxGridFitMode wxGrid::GetCellFitMode( int row, int col ) const
{
wxGridCellAttr *attr = GetCellAttr(row, col);
bool allow = attr->GetOverflow();
wxGridFitMode fitMode = attr->GetFitMode();
attr->DecRef();
return allow;
return fitMode;
}
wxGrid::CellSpan
@@ -8854,12 +8893,12 @@ void wxGrid::SetCellAlignment( int row, int col, int horiz, int vert )
}
}
void wxGrid::SetCellOverflow( int row, int col, bool allow )
void wxGrid::SetCellFitMode( int row, int col, wxGridFitMode fitMode )
{
if ( CanHaveAttributes() )
{
wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
attr->SetOverflow(allow);
attr->SetFitMode(fitMode);
attr->DecRef();
}
}

View File

@@ -148,15 +148,12 @@ void wxGridCellDateRenderer::Draw(wxGrid& grid,
SetTextColoursAndFont(grid, attr, dc, isSelected);
// draw the text right aligned by default
int hAlign = wxALIGN_RIGHT,
vAlign = wxALIGN_INVALID;
attr.GetNonDefaultAlignment(&hAlign, &vAlign);
wxRect rect = rectCell;
rect.Inflate(-1);
grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign);
// draw the text right aligned by default
grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, attr,
wxALIGN_RIGHT);
}
wxSize wxGridCellDateRenderer::GetBestSize(wxGrid& grid,
@@ -246,15 +243,12 @@ void wxGridCellEnumRenderer::Draw(wxGrid& grid,
SetTextColoursAndFont(grid, attr, dc, isSelected);
// draw the text right aligned by default
int hAlign = wxALIGN_RIGHT,
vAlign = wxALIGN_INVALID;
attr.GetNonDefaultAlignment(&hAlign, &vAlign);
wxRect rect = rectCell;
rect.Inflate(-1);
grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign);
// draw the text right aligned by default
grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, attr,
wxALIGN_RIGHT);
}
wxSize wxGridCellEnumRenderer::GetBestSize(wxGrid& grid,
@@ -308,6 +302,8 @@ wxGridCellAutoWrapStringRenderer::Draw(wxGrid& grid,
wxRect rect = rectCell;
rect.Inflate(-1);
// Do not use here the overload taking the attribute, as this would
// ellipsize the text, which is never necessary with this renderer.
grid.DrawTextRectangle(dc, GetTextLines(grid,dc,attr,rect,row,col),
rect, horizAlign, vertAlign);
}
@@ -595,11 +591,11 @@ void wxGridCellStringRenderer::Draw(wxGrid& grid,
// erase only this cells background, overflow cells should have been erased
wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected);
int hAlign, vAlign;
attr.GetAlignment(&hAlign, &vAlign);
if (attr.GetOverflow())
{
int hAlign, vAlign;
attr.GetAlignment(&hAlign, &vAlign);
int overflowCols = 0;
int cols = grid.GetNumberCols();
int best_width = GetBestSize(grid,attr,dc,row,col).GetWidth();
@@ -675,7 +671,7 @@ void wxGridCellStringRenderer::Draw(wxGrid& grid,
SetTextColoursAndFont(grid, attr, dc, isSelected);
grid.DrawTextRectangle(dc, grid.GetCellValue(row, col),
rect, hAlign, vAlign);
rect, attr);
}
// ----------------------------------------------------------------------------
@@ -709,15 +705,12 @@ void wxGridCellNumberRenderer::Draw(wxGrid& grid,
SetTextColoursAndFont(grid, attr, dc, isSelected);
// draw the text right aligned by default
int hAlign = wxALIGN_RIGHT,
vAlign = wxALIGN_INVALID;
attr.GetNonDefaultAlignment(&hAlign, &vAlign);
wxRect rect = rectCell;
rect.Inflate(-1);
grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign);
// draw the text right aligned by default
grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, attr,
wxALIGN_RIGHT);
}
wxSize wxGridCellNumberRenderer::GetBestSize(wxGrid& grid,
@@ -824,15 +817,12 @@ void wxGridCellFloatRenderer::Draw(wxGrid& grid,
SetTextColoursAndFont(grid, attr, dc, isSelected);
// draw the text right aligned by default
int hAlign = wxALIGN_RIGHT,
vAlign = wxALIGN_INVALID;
attr.GetNonDefaultAlignment(&hAlign, &vAlign);
wxRect rect = rectCell;
rect.Inflate(-1);
grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign);
// draw the text right aligned by default
grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, attr,
wxALIGN_RIGHT);
}
wxSize wxGridCellFloatRenderer::GetBestSize(wxGrid& grid,