Add support for "activatable" editors to wxGrid

This is useful for editors which don't really need to show any control
as they can change their value directly and will be used to reimplement
wxGridCellBoolEditor in the upcoming commits.
This commit is contained in:
Vadim Zeitlin 2020-06-30 02:25:14 +02:00
parent 73532c4d95
commit 3cc3236f10
3 changed files with 347 additions and 13 deletions

View File

@ -233,6 +233,130 @@ public:
// Smart pointer to wxGridCellRenderer, calling DecRef() on it automatically.
typedef wxObjectDataPtr<wxGridCellRenderer> wxGridCellRendererPtr;
// ----------------------------------------------------------------------------
// Helper classes used by wxGridCellEditor::TryActivate() and DoActivate().
// ----------------------------------------------------------------------------
// This class represents a source of cell activation, which may be either a
// user event (mouse or keyboard) or the program itself.
//
// Note that objects of this class are supposed to be ephemeral and so store
// pointers to the events specified when creating them, which are supposed to
// have life-time greater than that of the objects of this class.
class wxGridActivationSource
{
public:
enum Origin
{
Program,
Key,
Mouse
};
// Factory functions, only used by the library itself.
static wxGridActivationSource FromProgram()
{
return wxGridActivationSource(Program, NULL);
}
static wxGridActivationSource From(const wxKeyEvent& event)
{
return wxGridActivationSource(Key, &event);
}
static wxGridActivationSource From(const wxMouseEvent& event)
{
return wxGridActivationSource(Mouse, &event);
}
// Accessors allowing to retrieve information about the source.
// Can be called for any object.
Origin GetOrigin() const { return m_origin; }
// Can be called for objects with Key origin only.
const wxKeyEvent& GetKeyEvent() const
{
wxASSERT( m_origin == Key );
return *static_cast<const wxKeyEvent*>(m_event);
}
// Can be called for objects with Mouse origin only.
const wxMouseEvent& GetMouseEvent() const
{
wxASSERT( m_origin == Mouse );
return *static_cast<const wxMouseEvent*>(m_event);
}
private:
wxGridActivationSource(Origin origin, const wxEvent* event)
: m_origin(origin),
m_event(event)
{
}
const Origin m_origin;
const wxEvent* const m_event;
};
// This class represents the result of TryActivate(), which may be either
// absence of any action (if activating wouldn't change the value anyhow),
// attempt to change the value to the specified one or just start normal
// editing, which is the default for the editors not supporting activation.
class wxGridActivationResult
{
public:
enum Action
{
Ignore,
Change,
ShowEditor
};
// Factory functions, only used by the library itself.
static wxGridActivationResult DoNothing()
{
return wxGridActivationResult(Ignore);
}
static wxGridActivationResult DoChange(const wxString& newval)
{
return wxGridActivationResult(Change, newval);
}
static wxGridActivationResult DoEdit()
{
return wxGridActivationResult(ShowEditor);
}
// Accessors allowing to retrieve information about the result.
// Can be called for any object.
Action GetAction() const { return m_action; }
// Can be called for objects with Change action type only.
const wxString& GetNewValue() const
{
wxASSERT( m_action == Change );
return m_newval;
}
private:
explicit
wxGridActivationResult(Action action, const wxString& newval = wxString())
: m_action(action),
m_newval(newval)
{
}
const Action m_action;
const wxString m_newval;
};
// ----------------------------------------------------------------------------
// wxGridCellEditor: This class is responsible for providing and manipulating
// the in-place edit controls for the grid. Instances of wxGridCellEditor
@ -335,6 +459,32 @@ public:
wxControl* GetControl() { return wxDynamicCast(m_control, wxControl); }
void SetControl(wxControl* control) { m_control = control; }
// Support for "activatable" editors: those change the value of the cell
// immediately, instead of creating an editor control and waiting for user
// input.
//
// See wxGridCellBoolEditor for an example of such editor.
// Override this function to return "Change" activation result from it to
// show that the editor supports activation. DoActivate() will be called if
// the cell changing event is not vetoed.
virtual
wxGridActivationResult
TryActivate(int WXUNUSED(row), int WXUNUSED(col),
wxGrid* WXUNUSED(grid),
const wxGridActivationSource& WXUNUSED(actSource))
{
return wxGridActivationResult::DoEdit();
}
virtual
void
DoActivate(int WXUNUSED(row), int WXUNUSED(col), wxGrid* WXUNUSED(grid))
{
wxFAIL_MSG( "Must be overridden if TryActivate() is overridden" );
}
protected:
// the dtor is private because only DecRef() can delete us
virtual ~wxGridCellEditor();
@ -2908,13 +3058,17 @@ private:
}
// Show/hide the cell editor for the current cell unconditionally.
void DoShowCellEditControl();
// Return false if the editor was activated instead of being shown and also
// sets m_cellEditCtrlEnabled to true when it returns true as a side effect.
bool DoShowCellEditControl(const wxGridActivationSource& actSource);
void DoHideCellEditControl();
// Unconditionally try showing the editor for the current cell.
//
// Returns false if the user code vetoed wxEVT_GRID_EDITOR_SHOWN.
bool DoEnableCellEditControl();
// Returns false if the user code vetoed wxEVT_GRID_EDITOR_SHOWN or if the
// editor was simply activated and won't be permanently shown.
bool DoEnableCellEditControl(const wxGridActivationSource& actSource);
// Unconditionally disable (accepting the changes) the editor.
void DoDisableCellEditControl();

View File

@ -447,6 +447,97 @@ public:
};
/**
Represents a source of cell activation, which may be either a user event
(mouse or keyboard) or the program itself.
An object of this class is passed to wxGridCellEditor::TryActivate() by the
library and the code overriding this method may use its GetOrigin() method
to determine how exactly the cell is being activated.
@since 3.1.4
*/
class wxGridActivationSource
{
public:
/// Result of GetOrigin().
enum Origin
{
/// Activated due to an explicit wxGrid::EnableCellEditControl() call.
Program,
/// Activated due to the user pressing a key, see GetKeyEvent().
Key,
/// Activated due to the user clicking on a cell, see GetMouseEvent().
Mouse
};
/// Get the origin of the activation.
Origin GetOrigin() const;
/**
Get the key event corresponding to the key press activating the cell.
This method can be called for objects with Key origin only, use
GetOrigin() to check for this first.
*/
const wxKeyEvent& GetKeyEvent() const;
/**
Get the mouse event corresponding to the click activating the cell.
This method can be called for objects with Mouse origin only, use
GetOrigin() to check for this first.
*/
const wxMouseEvent& GetMouseEvent() const;
};
/**
Represents the result of wxGridCellEditor::TryActivate().
Editors overriding wxGridCellEditor::TryActivate() must use one of
DoNothing(), DoChange() or DoEdit() methods to return an object of this
type corresponding to the desired action.
@since 3.1.4
*/
class wxGridActivationResult
{
public:
/**
Indicate that nothing should be done and the cell shouldn't be edited
at all.
Note that this is different from DoEdit() and may be useful when the
value of the cell wouldn't change if it were activated anyhow, e.g.
because the key or mouse event carried by wxGridActivationSource would
leave the cell value unchanged.
*/
static wxGridActivationResult DoNothing();
/**
Indicate that activating the cell is possible and would change its
value to the given one.
This is the method to call for activatable editors, using it will
result in changing the value of the cell to @a newval without showing
the editor control at all.
Note that the change may still be vetoed by wxEVT_GRID_CELL_CHANGING
handler.
*/
static wxGridActivationResult DoChange(const wxString& newval);
/**
Indicate that the editor control should be shown and the cell should be
edited normally.
This is the default return value of wxGridCellEditor::TryActivate().
*/
static wxGridActivationResult DoEdit();
};
/**
@class wxGridCellEditor
@ -456,6 +547,12 @@ public:
the cell attributes for individual cells, rows, columns, or even for the
entire grid.
Normally wxGridCellEditor shows some UI control allowing the user to edit
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.
@library{wxcore}
@category{grid}
@ -627,6 +724,41 @@ public:
void SetControl(wxControl* control);
/**
Function allowing to create an "activatable" editor.
As explained in this class description, activatable editors don't show
any edit control but change the cell value directly, when it is
activated (by any way described by wxGridActivationSource).
To create such editor, this method must be overridden to return
wxGridActivationResult::DoChange() passing it the new value of the
cell. If the change is not vetoed by wxEVT_GRID_CELL_CHANGING handler,
DoActivate() will be called to actually change the value, so it must be
overridden as well if TryActivate() is overridden.
By default, wxGridActivationResult::DoEdit() is returned, meaning that
this is a normal editor, using an edit control for changing the cell
value.
@since 3.1.4
*/
virtual wxGridActivationResult
TryActivate(int row, int col, wxGrid* grid,
const wxGridActivationSource& actSource);
/**
Function which must be overridden for "activatable" editors.
If TryActivate() is overridden to return "change" action, this function
will be called to actually apply this change. Note that it is not
passed the value to apply, as it is assumed that the editor class
stores this value as a member variable anyhow.
@since 3.1.4
*/
virtual void DoActivate(int row, int col, wxGrid* grid);
protected:
/**

View File

@ -4664,7 +4664,7 @@ wxGrid::DoGridCellLeftUp(wxMouseEvent& event,
{
ClearSelection();
if ( DoEnableCellEditControl() )
if ( DoEnableCellEditControl(wxGridActivationSource::From(event)) )
GetCurrentCellEditorPtr()->StartingClick();
m_waitForSlowClick = false;
@ -5969,7 +5969,8 @@ void wxGrid::OnChar( wxKeyEvent& event )
// ensure cell is visble
MakeCellVisible(m_currentCellCoords);
if ( DoEnableCellEditControl() && !specialEditKey )
if ( DoEnableCellEditControl(wxGridActivationSource::From(event))
&& !specialEditKey )
editor->StartingKey(event);
}
else
@ -7149,7 +7150,7 @@ void wxGrid::EnableCellEditControl( bool enable )
// this should be checked by the caller!
wxCHECK_RET( CanEnableCellControl(), wxT("can't enable editing for this cell!") );
DoEnableCellEditControl();
DoEnableCellEditControl(wxGridActivationSource::FromProgram());
}
else
{
@ -7158,14 +7159,20 @@ void wxGrid::EnableCellEditControl( bool enable )
}
}
bool wxGrid::DoEnableCellEditControl()
bool wxGrid::DoEnableCellEditControl(const wxGridActivationSource& actSource)
{
if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 )
return false;
m_cellEditCtrlEnabled = true;
if ( !DoShowCellEditControl(actSource) )
{
// We have to send the HIDDEN event matching the SHOWN one above as the
// user code may reasonably expect always getting them in pairs, so do
// it even if the editor hadn't really been shown at all.
SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
DoShowCellEditControl();
return false;
}
return true;
}
@ -7217,16 +7224,57 @@ void wxGrid::ShowCellEditControl()
return;
}
DoShowCellEditControl();
DoShowCellEditControl(wxGridActivationSource::FromProgram());
}
}
void wxGrid::DoShowCellEditControl()
bool wxGrid::DoShowCellEditControl(const wxGridActivationSource& actSource)
{
wxRect rect = CellToRect( m_currentCellCoords );
int row = m_currentCellCoords.GetRow();
int col = m_currentCellCoords.GetCol();
wxGridCellAttrPtr attr = GetCellAttrPtr(row, col);
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col);
const wxGridActivationResult&
res = editor->TryActivate(row, col, this, actSource);
switch ( res.GetAction() )
{
case wxGridActivationResult::Change:
// This is somewhat similar to what DoSaveEditControlValue() does.
// but we don't allow vetoing CHANGED event here as this code is
// new and shouldn't have to support this obsolete usage.
if ( SendEvent(wxEVT_GRID_CELL_CHANGING, res.GetNewValue()) != -1 )
{
const wxString& oldval = GetCellValue(m_currentCellCoords);
editor->DoActivate(row, col, this);
// Show the new cell value.
RefreshBlock(m_currentCellCoords, m_currentCellCoords);
if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 )
{
wxFAIL_MSG( "Vetoing wxEVT_GRID_CELL_CHANGED is ignored" );
}
}
wxFALLTHROUGH;
case wxGridActivationResult::Ignore:
// In any case, don't start editing normally.
return false;
case wxGridActivationResult::ShowEditor:
// Continue normally.
break;
}
// It's not enabled just yet, but will be soon, and we need to set it
// before generating any events in case their user-defined handlers decide
// to call EnableCellEditControl() to avoid reentrancy problems.
m_cellEditCtrlEnabled = true;
wxGridWindow *gridWindow = CellToGridWindow(row, col);
// if this is part of a multicell, find owner (topleft)
@ -7251,8 +7299,6 @@ void wxGrid::DoShowCellEditControl()
rect.Deflate(1, 1);
#endif
wxGridCellAttrPtr attr = GetCellAttrPtr(row, col);
wxGridCellEditorPtr editor = attr->GetEditorPtr(this, row, col);
if ( !editor->IsCreated() )
{
editor->Create(gridWindow, wxID_ANY,
@ -7348,6 +7394,8 @@ void wxGrid::DoShowCellEditControl()
editor->BeginEdit(row, col, this);
editor->SetCellAttr(NULL);
return true;
}
void wxGrid::HideCellEditControl()