Add wxGridCellActivatableEditor and show it in the grid sample

Add a helper class making it easier to define activation-only editors
and use it to implement MyGridStarEditor in the sample, showing a
typical example of using such editor.
This commit is contained in:
Vadim Zeitlin 2020-06-30 02:30:36 +02:00
parent 3cc3236f10
commit ca84560350
3 changed files with 193 additions and 1 deletions

View File

@ -523,6 +523,34 @@ protected:
// Smart pointer to wxGridCellEditor, calling DecRef() on it automatically.
typedef wxObjectDataPtr<wxGridCellEditor> wxGridCellEditorPtr;
// Base class for editors that can be only activated and not edited normally.
class wxGridCellActivatableEditor : public wxGridCellEditor
{
public:
// In this class these methods must be overridden.
virtual wxGridActivationResult
TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource) = 0;
virtual void DoActivate(int row, int col, wxGrid* grid) = 0;
// All the other methods that normally must be implemented in an editor are
// defined as just stubs below, as they should be never called.
virtual void Create(wxWindow*, wxWindowID, wxEvtHandler*) wxOVERRIDE
{ wxFAIL; }
virtual void BeginEdit(int, int, wxGrid*) wxOVERRIDE
{ wxFAIL; }
virtual bool EndEdit(int, int, const wxGrid*,
const wxString&, wxString*) wxOVERRIDE
{ wxFAIL; return false; }
virtual void ApplyEdit(int, int, wxGrid*) wxOVERRIDE
{ wxFAIL; }
virtual void Reset() wxOVERRIDE
{ wxFAIL; }
virtual wxString GetValue() const wxOVERRIDE
{ wxFAIL; return wxString(); }
};
// ----------------------------------------------------------------------------
// wxGridHeaderRenderer and company: like wxGridCellRenderer but for headers
// ----------------------------------------------------------------------------

View File

@ -551,7 +551,10 @@ public:
the cell, but starting with wxWidgets 3.1.4 it's also possible to define
"activatable" cell editors, that change the value of the cell directly when
it's activated (typically by pressing Space key or clicking on it), see
TryActivate() method.
TryActivate() method. Note that when implementing an editor which is always
activatable, i.e. never shows any in-place editor, it is more convenient to
derive its class from wxGridCellActivatableEditor than from wxGridCellEditor
itself.
@library{wxcore}
@category{grid}
@ -767,6 +770,34 @@ protected:
virtual ~wxGridCellEditor();
};
/**
Base class for activatable editors.
Inheriting from this class makes it simpler to implement editors that
support only activation, but not in-place editing, as they only need to
implement TryActivate(), DoActivate() and Clone() methods, but not all the
other pure virtual methods of wxGridCellEditor.
@since 3.1.4
*/
class wxGridCellActivatableEditor : public wxGridCellEditor
{
public:
/**
Same method as in wxGridCellEditor, but pure virtual.
Note that the implementation of this method must never return
wxGridActivationResult::DoEdit() for the editors inheriting from this
class, as it doesn't support normal editing.
*/
virtual wxGridActivationResult
TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource) = 0;
/// Same method as in wxGridCellEditor, but pure virtual.
virtual void DoActivate(int row, int col, wxGrid* grid) = 0;
};
/**
Smart pointer wrapping wxGridCellEditor.

View File

@ -121,6 +121,135 @@ private:
wxDECLARE_NO_COPY_CLASS(CustomColumnHeadersProvider);
};
// ----------------------------------------------------------------------------
// Custom wxGrid renderer and editor for showing stars as used for rating
// ----------------------------------------------------------------------------
// Max number of stars shown by MyGridStarRenderer.
static const int MAX_STARS = 5;
// Helper function returning the number between 0 and MAX_STARS corresponding
// to the value of the cell.
static int GetStarValue(wxGrid& grid, int row, int col)
{
unsigned long n = 0;
if ( !grid.GetCellValue(row, col).ToULong(&n) || n > MAX_STARS )
n = 0;
return static_cast<int>(n);
}
// Another helper returning the string containing the appropriate number of
// black and white stars.
static wxString GetStarString(int numBlackStars)
{
const wxUniChar BLACK_STAR = 0x2605;
const wxUniChar WHITE_STAR = 0x2606;
return wxString(BLACK_STAR, numBlackStars) +
wxString(WHITE_STAR, MAX_STARS - numBlackStars);
}
// Renders the value of the cell, which is supposed to be a number between 1
// and 5, as a sequence of that number of black stars followed by the number of
// white stars needed to have 5 stars in total.
class MyGridStarRenderer : public wxGridCellRenderer
{
public:
virtual void Draw(wxGrid& grid,
wxGridCellAttr& attr,
wxDC& dc,
const wxRect& rect,
int row, int col,
bool isSelected) wxOVERRIDE
{
wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
grid.DrawTextRectangle(dc, GetStarString(GetStarValue(grid, row, col)),
rect, attr);
}
virtual wxSize GetBestSize(wxGrid& WXUNUSED(grid),
wxGridCellAttr& attr,
wxDC& dc,
int WXUNUSED(row),
int WXUNUSED(col)) wxOVERRIDE
{
dc.SetFont(attr.GetFont());
return dc.GetTextExtent(GetStarString(MAX_STARS));
}
virtual wxGridCellRenderer *Clone() const wxOVERRIDE
{
return new MyGridStarRenderer();
}
};
// Activatable editor cycling the number of stars on each activation.
class MyGridStarEditor : public wxGridCellActivatableEditor
{
public:
virtual wxGridActivationResult
TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource) wxOVERRIDE
{
int numStars = -1;
switch ( actSource.GetOrigin() )
{
case wxGridActivationSource::Program:
// It isn't really possible to programmatically start editing a
// cell using this editor.
return wxGridActivationResult::DoNothing();
case wxGridActivationSource::Key:
switch ( actSource.GetKeyEvent().GetKeyCode() )
{
case '0': numStars = 0; break;
case '1': numStars = 1; break;
case '2': numStars = 2; break;
case '3': numStars = 3; break;
case '4': numStars = 4; break;
case '5': numStars = 5; break;
case ' ':
// Use space key to cycle over the values.
break;
default:
return wxGridActivationResult::DoNothing();
}
break;
case wxGridActivationSource::Mouse:
// Ideally we should use the mouse event position to determine
// on which star the user clicked, but for now keep it simple
// and just cycle through the star value.
break;
}
if ( numStars == -1 )
numStars = (GetStarValue(*grid, row, col) + 1) % (MAX_STARS + 1);
m_value.Printf("%d", numStars);
return wxGridActivationResult::DoChange(m_value);
}
virtual void DoActivate(int row, int col, wxGrid* grid) wxOVERRIDE
{
grid->SetCellValue(row, col, m_value);
}
virtual wxGridCellEditor *Clone() const
{
return new MyGridStarEditor();
}
private:
wxString m_value;
};
// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------
@ -517,6 +646,10 @@ GridFrame::GridFrame()
grid->SetCellAlignment(4, 4, wxALIGN_CENTRE, wxALIGN_CENTRE);
grid->SetCellRenderer(4, 4, new MyGridCellRenderer);
grid->SetCellValue(4, 5, "3");
grid->SetCellRenderer(4, 5, new MyGridStarRenderer);
grid->SetCellEditor(4, 5, new MyGridStarEditor);
grid->SetCellRenderer(3, 0, new wxGridCellBoolRenderer);
grid->SetCellEditor(3, 0, new wxGridCellBoolEditor);
grid->SetCellBackgroundColour(3, 0, wxColour(255, 127, 127));