Allow custom wxDataViewCtrl renderers to easily use attributes.

Set up the DC passed to wxDataViewCustomRenderer::Render() to use the font and
colour defined by the item attribute by default so that any calls to
RenderText() from it will use them automatically.

Also added public wxDataViewCustomRenderer::GetAttr() to allow retrieving the
attribute explicitly in Render().

The column using custom renderer in the dataview sample now works as expected
in the generic version; the native ones will be corrected in the upcoming
commits.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62590 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2009-11-10 17:41:11 +00:00
parent 6eec70b984
commit 62265c2c67
12 changed files with 201 additions and 263 deletions

View File

@@ -109,13 +109,16 @@ public:
void SetOwner( wxDataViewColumn *owner ) { m_owner = owner; }
wxDataViewColumn* GetOwner() const { return m_owner; }
// renderer properties:
// renderer value and attributes: SetValue() and SetAttr() are called
// before a cell is rendered using this renderer
virtual bool SetValue(const wxVariant& value) = 0;
virtual bool GetValue(wxVariant& value) const = 0;
virtual bool SetValue( const wxVariant& WXUNUSED(value) ) = 0;
virtual bool GetValue( wxVariant& WXUNUSED(value) ) const = 0;
virtual void SetAttr(const wxDataViewItemAttr& WXUNUSED(attr)) { }
wxString GetVariantType() const { return m_variantType; }
// renderer properties:
virtual void SetMode( wxDataViewCellMode mode ) = 0;
virtual wxDataViewCellMode GetMode() const = 0;
@@ -204,6 +207,12 @@ public:
}
// Render the item using the current value (returned by GetValue()).
virtual bool Render(wxRect cell, wxDC *dc, int state) = 0;
// Return the size of the item appropriate to its current value.
virtual wxSize GetSize() const = 0;
// Define virtual function which are called when the item is activated
// (double-clicked or Enter is pressed on it), clicked or the user starts
// to drag it: by default they all simply return false indicating that the
@@ -230,7 +239,34 @@ public:
{ return false; }
// Helper which can be used by Render() implementation in the derived
// classes: it will draw the text in the same manner as the standard
// renderers do.
virtual void RenderText(const wxString& text,
int xoffset,
wxRect cell,
wxDC *dc,
int state);
// Override the base class virtual method to simply store the attribute so
// that it can be accessed using GetAttr() from Render() if needed.
virtual void SetAttr(const wxDataViewItemAttr& attr) { m_attr = attr; }
const wxDataViewItemAttr& GetAttr() const { return m_attr; }
// Implementation only from now on
// Retrieve the DC to use for drawing. This is implemented in derived
// platform-specific classes.
virtual wxDC *GetDC() = 0;
// Prepare DC to use attributes and call Render().
void WXCallRender(wxRect rect, wxDC *dc, int state);
private:
wxDataViewItemAttr m_attr;
wxDECLARE_NO_COPY_CLASS(wxDataViewCustomRendererBase);
};

View File

@@ -24,43 +24,8 @@ public:
int align = wxDVR_DEFAULT_ALIGNMENT );
virtual ~wxDataViewRenderer();
// these methods are used to draw the cell contents, Render() doesn't care
// about the attributes while RenderWithAttr() does -- override it if you
// want to take the attributes defined for this cell into account, otherwise
// overriding Render() is enough
virtual bool Render( wxRect cell, wxDC *dc, int state ) = 0;
// NB: RenderWithAttr() also has more standard parameter order and types
virtual bool
RenderWithAttr(wxDC& dc,
const wxRect& rect,
int align, // combination of horizontal and vertical
const wxDataViewItemAttr *attr, // may be NULL if none
int state);
virtual wxSize GetSize() const = 0;
virtual wxDC *GetDC();
// Draw the text using the provided attributes
void RenderText(wxDC& dc,
const wxRect& rect,
int align,
const wxString& text,
const wxDataViewItemAttr *attr, // may be NULL if none
int state,
int xoffset = 0);
// Overload using standard attributes
void RenderText(const wxString& text,
int xoffset,
wxRect cell,
wxDC *dc,
int state)
{
RenderText(*dc, cell, wxALIGN_NOT, text, NULL, state, xoffset);
}
virtual void SetAlignment( int align );
virtual int GetAlignment() const;
@@ -75,7 +40,6 @@ public:
{ return m_mode; }
// implementation
int CalculateAlignment() const;
// this is a replacement for dynamic_cast<wxDataViewCustomRenderer> in the
// code checking whether the renderer is interested in mouse events, it's
@@ -89,18 +53,6 @@ public:
// never be called
virtual wxDataViewCustomRenderer *WXGetAsCustom() { return NULL; }
protected:
// This is just a convenience for the derived classes overriding
// RenderWithAttr() to avoid repeating the same wxFAIL_MSG() in all of them
bool DummyRender(wxRect WXUNUSED(cell),
wxDC * WXUNUSED(dc),
int WXUNUSED(state))
{
wxFAIL_MSG("shouldn't be called at all, use RenderWithAttr() instead");
return false;
}
private:
int m_align;
wxDataViewCellMode m_mode;

View File

@@ -44,17 +44,8 @@ public:
bool SetValue( const wxVariant &value );
bool GetValue( wxVariant &value ) const;
virtual bool RenderWithAttr(wxDC& dc,
const wxRect& rect,
int align,
const wxDataViewItemAttr *attr,
int state);
virtual bool Render(wxRect cell, wxDC *dc, int state)
{
return DummyRender(cell, dc, state);
}
wxSize GetSize() const;
virtual bool Render(wxRect cell, wxDC *dc, int state);
virtual wxSize GetSize() const;
// in-place editing
virtual bool HasEditorCtrl() const;
@@ -135,15 +126,7 @@ public:
bool SetValue( const wxVariant &value );
bool GetValue( wxVariant& value ) const;
virtual bool RenderWithAttr(wxDC& dc,
const wxRect& rect,
int align,
const wxDataViewItemAttr *attr,
int state);
virtual bool Render(wxRect cell, wxDC *dc, int state)
{
return DummyRender(cell, dc, state);
}
virtual bool Render(wxRect cell, wxDC *dc, int state);
virtual wxSize GetSize() const;
private:
@@ -168,15 +151,7 @@ public:
bool SetValue( const wxVariant &value );
bool GetValue( wxVariant &value ) const;
virtual bool RenderWithAttr(wxDC& dc,
const wxRect& rect,
int align,
const wxDataViewItemAttr *attr,
int state);
virtual bool Render(wxRect cell, wxDC *dc, int state)
{
return DummyRender(cell, dc, state);
}
virtual bool Render(wxRect cell, wxDC *dc, int state);
virtual wxSize GetSize() const;
virtual bool HasEditorCtrl() const { return true; }

View File

@@ -100,15 +100,15 @@ public:
virtual ~wxDataViewCustomRenderer();
virtual bool Render( wxRect cell, wxDC *dc, int state ) = 0;
void RenderText( const wxString &text, int xoffset, wxRect cell, wxDC *dc, int state );
virtual wxSize GetSize() const = 0;
// Create DC on request
virtual wxDC *GetDC();
// override the base class function to use GTK text cell renderer
virtual void RenderText(const wxString& text,
int xoffset,
wxRect cell,
wxDC *dc,
int state);
protected:

View File

@@ -24,12 +24,6 @@ public:
virtual ~wxDataViewCustomRenderer();
void RenderText( const wxString &text, int xoffset, wxRect cell, wxDC *dc, int state );
virtual wxSize GetSize() const = 0;
virtual bool Render(wxRect cell, wxDC* dc, int state) = 0;
// implementation only
// -------------------

View File

@@ -1435,9 +1435,20 @@ public:
const wxVariant& value);
/**
Create DC on request. Internal.
Return the attribute to be used for rendering.
This function may be called from Render() implementation to use the
attributes defined for the item if the renderer supports them.
Notice that when Render() is called, the wxDC object passed to it is
already set up to use the correct attributes (e.g. its font is set to
bold or italic version if wxDataViewItemAttr::GetBold() or GetItalic()
returns true) so it may not be necessary to call it explicitly if you
only want to render text using the items attributes.
@since 2.9.1
*/
virtual wxDC* GetDC();
const wxDataViewItemAttr& GetAttr() const;
/**
Return size required to show content.

View File

@@ -694,6 +694,113 @@ bool wxDataViewRendererBase::FinishEditing()
return true;
}
// ----------------------------------------------------------------------------
// wxDataViewCustomRendererBase
// ----------------------------------------------------------------------------
void
wxDataViewCustomRendererBase::WXCallRender(wxRect rectCell, wxDC *dc, int state)
{
wxCHECK_RET( dc, "no DC to draw on in custom renderer?" );
// adjust the rectangle ourselves to account for the alignment
wxRect rectItem = rectCell;
const int align = GetAlignment();
if ( align != wxDVR_DEFAULT_ALIGNMENT )
{
const wxSize size = GetSize();
// take alignment into account only if there is enough space, otherwise
// show as much contents as possible
//
// notice that many existing renderers (e.g. wxDataViewSpinRenderer)
// return hard-coded size which can be more than they need and if we
// trusted their GetSize() we'd draw the text out of cell bounds
// entirely
if ( size.x >= 0 && size.x < rectCell.width )
{
if ( align & wxALIGN_CENTER_HORIZONTAL )
rectItem.x += (rectCell.width - size.x)/2;
else if ( align & wxALIGN_RIGHT )
rectItem.x += rectCell.width - size.x;
// else: wxALIGN_LEFT is the default
rectItem.width = size.x;
}
if ( size.y >= 0 && size.y < rectCell.height )
{
if ( align & wxALIGN_CENTER_VERTICAL )
rectItem.y += (rectCell.height - size.y)/2;
else if ( align & wxALIGN_BOTTOM )
rectItem.y += rectCell.height - size.y;
// else: wxALIGN_TOP is the default
rectItem.height = size.y;
}
}
// set up the DC attributes
// override custom foreground with the standard one for the selected items
// because we currently don't allow changing the selection background and
// custom colours may be unreadable on it
wxColour col;
if ( state & wxDATAVIEW_CELL_SELECTED )
col = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
else if ( m_attr.HasColour() )
col = m_attr.GetColour();
else // use default foreground
col = GetOwner()->GetOwner()->GetForegroundColour();
wxDCTextColourChanger changeFg(*dc, col);
wxDCFontChanger changeFont(*dc);
if ( m_attr.HasFont() )
{
wxFont font(dc->GetFont());
if ( m_attr.GetBold() )
font.MakeBold();
if ( m_attr.GetItalic() )
font.MakeItalic();
changeFont.Set(font);
}
Render(rectItem, dc, state);
}
void
wxDataViewCustomRendererBase::RenderText(const wxString& text,
int xoffset,
wxRect rect,
wxDC *dc,
int WXUNUSED(state))
{
wxRect rectText = rect;
rectText.x += xoffset;
rectText.width -= xoffset;
// check if we want to ellipsize the text if it doesn't fit
wxString ellipsizedText;
if ( GetEllipsizeMode() != wxELLIPSIZE_NONE )
{
ellipsizedText = wxControl::Ellipsize
(
text,
*dc,
GetEllipsizeMode(),
rect.width,
wxELLIPSIZE_FLAGS_NONE
);
}
dc->DrawLabel(ellipsizedText.empty() ? text : ellipsizedText,
rectText, GetAlignment());
}
//-----------------------------------------------------------------------------
// wxDataViewEditorCtrlEvtHandler
//-----------------------------------------------------------------------------

View File

@@ -644,123 +644,6 @@ int wxDataViewRenderer::GetAlignment() const
return m_align;
}
int wxDataViewRenderer::CalculateAlignment() const
{
if (m_align == wxDVR_DEFAULT_ALIGNMENT)
{
if (GetOwner() == NULL)
return wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL;
return GetOwner()->GetAlignment() | wxALIGN_CENTRE_VERTICAL;
}
return m_align;
}
bool
wxDataViewRenderer::RenderWithAttr(wxDC& dc,
const wxRect& cell_rect,
int align,
const wxDataViewItemAttr *WXUNUSED(attr),
int state)
{
// adjust the rectangle ourselves to account for the alignment
wxRect item_rect = cell_rect;
if ( align )
{
const wxSize size = GetSize();
// take alignment into account only if there is enough space, otherwise
// show as much contents as possible
//
// notice that many existing renderers (e.g. wxDataViewSpinRenderer)
// return hard-coded size which can be more than they need and if we
// trusted their GetSize() we'd draw the text out of cell bounds
// entirely
if ( size.x < cell_rect.width )
{
if (align & wxALIGN_CENTER_HORIZONTAL)
item_rect.x += (cell_rect.width - size.x)/2;
else if (align & wxALIGN_RIGHT)
item_rect.x += cell_rect.width - size.x;
// else: wxALIGN_LEFT is the default
item_rect.width = size.x;
}
if ( size.y < cell_rect.height )
{
if (align & wxALIGN_CENTER_VERTICAL)
item_rect.y += (cell_rect.height - size.y)/2;
else if (align & wxALIGN_BOTTOM)
item_rect.y += cell_rect.height - size.y;
// else: wxALIGN_TOP is the default
item_rect.height = size.y;
}
}
return Render(item_rect, &dc, state);
}
void
wxDataViewRenderer::RenderText(wxDC& dc,
const wxRect& rect,
int align,
const wxString& text,
const wxDataViewItemAttr *attr,
int state,
int xoffset)
{
// override custom foreground with the standard one for the selected items
// because we currently don't allow changing the selection background and
// custom colours may be unreadable on it
wxColour col;
if ( state & wxDATAVIEW_CELL_SELECTED )
col = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
else if ( attr && attr->HasColour() )
col = attr->GetColour();
else // use default foreground
col = GetOwner()->GetOwner()->GetForegroundColour();
wxDCTextColourChanger changeFg(dc, col);
wxDCFontChanger changeFont(dc);
if ( attr && attr->HasFont() )
{
wxFont font(dc.GetFont());
if ( attr->GetBold() )
font.MakeBold();
if ( attr->GetItalic() )
font.MakeItalic();
changeFont.Set(font);
}
wxRect rectText = rect;
rectText.x += xoffset;
rectText.width -= xoffset;
// check if we want to ellipsize the text if it doesn't fit
wxString ellipsizedText;
if ( GetEllipsizeMode() != wxELLIPSIZE_NONE )
{
ellipsizedText = wxControl::Ellipsize
(
text,
dc,
GetEllipsizeMode(),
rect.width,
wxELLIPSIZE_FLAGS_NONE
);
}
dc.DrawLabel(ellipsizedText.empty() ? text : ellipsizedText,
rectText, align);
}
// ---------------------------------------------------------
// wxDataViewCustomRenderer
// ---------------------------------------------------------
@@ -823,14 +706,9 @@ bool wxDataViewTextRenderer::GetValueFromEditorCtrl( wxControl *editor, wxVarian
return true;
}
bool
wxDataViewTextRenderer::RenderWithAttr(wxDC& dc,
const wxRect& rect,
int align,
const wxDataViewItemAttr *attr,
int state)
bool wxDataViewTextRenderer::Render(wxRect rect, wxDC *dc, int state)
{
RenderText(dc, rect, align, m_text, attr, state);
RenderText(m_text, 0, rect, dc, state);
return true;
}
@@ -976,24 +854,23 @@ bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
return true;
}
bool wxDataViewProgressRenderer::RenderWithAttr(wxDC& dc,
const wxRect& rect,
int WXUNUSED(align),
const wxDataViewItemAttr *attr,
int WXUNUSED(state))
bool
wxDataViewProgressRenderer::Render(wxRect rect, wxDC *dc, int WXUNUSED(state))
{
// deflat the rect to leave a small border between bars in adjacent rows
// deflate the rect to leave a small border between bars in adjacent rows
wxRect bar = rect.Deflate(0, 1);
dc.SetBrush( *wxTRANSPARENT_BRUSH );
dc.SetPen( *wxBLACK_PEN );
dc.DrawRectangle( bar );
dc->SetBrush( *wxTRANSPARENT_BRUSH );
dc->SetPen( *wxBLACK_PEN );
dc->DrawRectangle( bar );
bar.width = (int)(bar.width * m_value / 100.);
dc.SetPen( *wxTRANSPARENT_PEN );
dc.SetBrush( attr && attr->HasColour() ? wxBrush(attr->GetColour())
dc->SetPen( *wxTRANSPARENT_PEN );
const wxDataViewItemAttr& attr = GetAttr();
dc->SetBrush( attr.HasColour() ? wxBrush(attr.GetColour())
: *wxBLUE_BRUSH );
dc.DrawRectangle( bar );
dc->DrawRectangle( bar );
return true;
}
@@ -1138,23 +1015,18 @@ bool wxDataViewIconTextRenderer::GetValue( wxVariant& WXUNUSED(value) ) const
return false;
}
bool
wxDataViewIconTextRenderer::RenderWithAttr(wxDC& dc,
const wxRect& rect,
int align,
const wxDataViewItemAttr *attr,
int state)
bool wxDataViewIconTextRenderer::Render(wxRect rect, wxDC *dc, int state)
{
int xoffset = 0;
const wxIcon& icon = m_value.GetIcon();
if ( icon.IsOk() )
{
dc.DrawIcon(icon, rect.x, rect.y + (rect.height - icon.GetHeight())/2);
dc->DrawIcon(icon, rect.x, rect.y + (rect.height - icon.GetHeight())/2);
xoffset = icon.GetWidth()+4;
}
RenderText(dc, rect, align, m_value.GetText(), attr, state, xoffset);
RenderText(m_value.GetText(), xoffset, rect, dc, state);
return true;
}
@@ -1640,15 +1512,15 @@ wxBitmap wxDataViewMainWindow::CreateItemBitmap( unsigned int row, int &indent )
model->GetValue( value, item, column->GetModelColumn());
cell->SetValue( value );
wxDataViewItemAttr attr;
model->GetAttr(item, column->GetModelColumn(), attr);
cell->SetAttr(attr);
wxRect item_rect(x, 0, width, height);
item_rect.Deflate(PADDING_RIGHTLEFT, 0);
// dc.SetClippingRegion( item_rect );
wxDataViewItemAttr attr;
const bool
hasAttr = model->GetAttr(item, column->GetModelColumn(), attr);
cell->RenderWithAttr(dc, item_rect, cell->CalculateAlignment(),
hasAttr ? &attr : NULL, 0);
cell->WXCallRender(item_rect, &dc, 0);
// dc.DestroyClippingRegion();
x += width;
@@ -1840,6 +1712,10 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
model->GetValue( value, dataitem, col->GetModelColumn());
cell->SetValue( value );
wxDataViewItemAttr attr;
model->GetAttr(dataitem, col->GetModelColumn(), attr);
cell->SetAttr(attr);
// update cell_rect
cell_rect.y = GetLineStart( item );
cell_rect.height = GetLineHeight( item );
@@ -1910,11 +1786,7 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
// make its own renderer and thus we cannot be sure of that.
wxDCClipper clip(dc, item_rect);
wxDataViewItemAttr attr;
const bool
hasAttr = model->GetAttr(dataitem, col->GetModelColumn(), attr);
cell->RenderWithAttr(dc, item_rect, cell->CalculateAlignment(),
hasAttr ? &attr : NULL, state);
cell->WXCallRender(item_rect, &dc, state);
}
cell_rect.x += cell_rect.width;

View File

@@ -1268,7 +1268,7 @@ gtk_wx_cell_renderer_render (GtkCellRenderer *renderer,
state |= wxDATAVIEW_CELL_INSENSITIVE;
if (flags & GTK_CELL_RENDERER_FOCUSED)
state |= wxDATAVIEW_CELL_FOCUSED;
cell->Render( renderrect, dc, state );
cell->WXCallRender( renderrect, dc, state );
}
}

View File

@@ -1701,7 +1701,7 @@ wxMacDataViewDataBrowserListViewControl::DataBrowserDrawItemProc(DataBrowserItem
{
// make sure that 'Render' can draw only in the allowed area:
dc->SetClippingRegion(content.left,content.top,content.right-content.left+1,content.bottom-content.top+1);
(void) (dataViewCustomRendererPtr->Render( cellrect, dc,
(void) (dataViewCustomRendererPtr->WXCallRender( cellrect, dc,
((state == kDataBrowserItemIsSelected) ? wxDATAVIEW_CELL_SELECTED : 0)));
dc->DestroyClippingRegion(); // probably not necessary
}

View File

@@ -1108,8 +1108,8 @@ outlineView:(NSOutlineView*)outlineView
[[self backgroundColor] set];
NSRectFill(cellFrame);
// TODO: attributes support
renderer->Render(wxFromNSRect(controlView, cellFrame), renderer->GetDC(), 0);
wxDC * const dc = renderer->GetDC();
renderer->WXCallRender(wxFromNSRect(controlView, cellFrame), dc, 0);
renderer->SetDC(NULL);
}

View File

@@ -303,15 +303,6 @@ wxDataViewCustomRenderer::~wxDataViewCustomRenderer()
delete m_DCPtr;
}
void wxDataViewCustomRenderer::RenderText( const wxString &text, int xoffset, wxRect cell, wxDC *dc, int state )
{
wxDataViewCtrl *view = GetOwner()->GetOwner();
wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ? *wxWHITE : view->GetForegroundColour();
dc->SetTextForeground(col);
dc->DrawText( text, cell.x + xoffset, cell.y + ((cell.height - dc->GetCharHeight()) / 2));
}
wxDC* wxDataViewCustomRenderer::GetDC()
{
if ((m_DCPtr == NULL) && (GetOwner() != NULL) && (GetOwner()->GetOwner() != NULL))