Make TAB behaviour in wxGrid more configurable.

Allow making TAB/Shift-TAB wrap to the next/previous row or going to the
next/previous control when the cursor is at the end/beginning of the current
row easily.

Also add wxEVT_GRID_TABBING event to allow customizing TAB behaviour even
further.

Update the sample to show the different possible standard behaviours and a
stupid example of a custom one (it would be probably more useful to implement
something a tad more realistic, e.g. tabbing to the next non-empty cell).

Closes #14711.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72672 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2012-10-13 22:55:18 +00:00
parent ac6a837eed
commit 1dc17bcafb
6 changed files with 193 additions and 20 deletions

View File

@@ -567,6 +567,7 @@ All (GUI):
- Add support for wxALWAYS_SHOW_SB style to wxScrolled<> (Catalin Raceanu). - Add support for wxALWAYS_SHOW_SB style to wxScrolled<> (Catalin Raceanu).
- Add wxTreeCtrl::EnableBellOnNoMatch() (Jonathan Dagresta). - Add wxTreeCtrl::EnableBellOnNoMatch() (Jonathan Dagresta).
- Implement incremental search in wxGenericListCtrl (Jonathan Dagresta). - Implement incremental search in wxGenericListCtrl (Jonathan Dagresta).
- Make TAB behaviour in wxGrid more flexible (Fulvio Senore).
wxGTK: wxGTK:

View File

@@ -918,6 +918,15 @@ public:
wxGridSelectRowsOrColumns = wxGridSelectRows | wxGridSelectColumns wxGridSelectRowsOrColumns = wxGridSelectRows | wxGridSelectColumns
}; };
// Different behaviour of the TAB key when the end (or the beginning, for
// Shift-TAB) of the current row is reached:
enum TabBehaviour
{
Tab_Stop, // Do nothing, this is default.
Tab_Wrap, // Move to the next (or previous) row.
Tab_Leave // Move to the next (or previous) control.
};
// creation and destruction // creation and destruction
// ------------------------ // ------------------------
@@ -1172,6 +1181,8 @@ public:
bool MoveCursorLeftBlock( bool expandSelection ); bool MoveCursorLeftBlock( bool expandSelection );
bool MoveCursorRightBlock( bool expandSelection ); bool MoveCursorRightBlock( bool expandSelection );
void SetTabBehaviour(TabBehaviour behaviour) { m_tabBehaviour = behaviour; }
// ------ label and gridline formatting // ------ label and gridline formatting
// //
@@ -2074,6 +2085,8 @@ protected:
bool m_editable; // applies to whole grid bool m_editable; // applies to whole grid
bool m_cellEditCtrlEnabled; // is in-place edit currently shown? bool m_cellEditCtrlEnabled; // is in-place edit currently shown?
TabBehaviour m_tabBehaviour; // determines how the TAB key behaves
void Init(); // common part of all ctors void Init(); // common part of all ctors
void Create(); void Create();
void CreateColumnWindow(); void CreateColumnWindow();
@@ -2241,6 +2254,8 @@ private:
void DoEndDragResizeCol(const wxMouseEvent& event); void DoEndDragResizeCol(const wxMouseEvent& event);
void DoEndMoveCol(int pos); void DoEndMoveCol(int pos);
// process a TAB keypress
void DoGridProcessTab(wxKeyboardState& kbdState);
// common implementations of methods defined for both rows and columns // common implementations of methods defined for both rows and columns
void DeselectLine(int line, const wxGridOperations& oper); void DeselectLine(int line, const wxGridOperations& oper);
@@ -2598,6 +2613,7 @@ wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_ADV, wxEVT_GRID_EDITOR_CREATED, wxGridEdit
wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_ADV, wxEVT_GRID_CELL_BEGIN_DRAG, wxGridEvent ); wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_ADV, wxEVT_GRID_CELL_BEGIN_DRAG, wxGridEvent );
wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_ADV, wxEVT_GRID_COL_MOVE, wxGridEvent ); wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_ADV, wxEVT_GRID_COL_MOVE, wxGridEvent );
wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_ADV, wxEVT_GRID_COL_SORT, wxGridEvent ); wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_ADV, wxEVT_GRID_COL_SORT, wxGridEvent );
wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_ADV, wxEVT_GRID_TABBING, wxGridEvent );
typedef void (wxEvtHandler::*wxGridEventFunction)(wxGridEvent&); typedef void (wxEvtHandler::*wxGridEventFunction)(wxGridEvent&);
typedef void (wxEvtHandler::*wxGridSizeEventFunction)(wxGridSizeEvent&); typedef void (wxEvtHandler::*wxGridSizeEventFunction)(wxGridSizeEvent&);
@@ -2648,6 +2664,7 @@ typedef void (wxEvtHandler::*wxGridEditorCreatedEventFunction)(wxGridEditorCreat
#define EVT_GRID_CMD_EDITOR_HIDDEN(id, fn) wx__DECLARE_GRIDEVT(EDITOR_HIDDEN, id, fn) #define EVT_GRID_CMD_EDITOR_HIDDEN(id, fn) wx__DECLARE_GRIDEVT(EDITOR_HIDDEN, id, fn)
#define EVT_GRID_CMD_EDITOR_CREATED(id, fn) wx__DECLARE_GRIDEDITOREVT(EDITOR_CREATED, id, fn) #define EVT_GRID_CMD_EDITOR_CREATED(id, fn) wx__DECLARE_GRIDEDITOREVT(EDITOR_CREATED, id, fn)
#define EVT_GRID_CMD_CELL_BEGIN_DRAG(id, fn) wx__DECLARE_GRIDEVT(CELL_BEGIN_DRAG, id, fn) #define EVT_GRID_CMD_CELL_BEGIN_DRAG(id, fn) wx__DECLARE_GRIDEVT(CELL_BEGIN_DRAG, id, fn)
#define EVT_GRID_CMD_TABBING(id, fn) wx__DECLARE_GRIDEVT(TABBING, id, fn)
// same as above but for any id (exists mainly for backwards compatibility but // same as above but for any id (exists mainly for backwards compatibility but
// then it's also true that you rarely have multiple grid in the same window) // then it's also true that you rarely have multiple grid in the same window)
@@ -2671,6 +2688,7 @@ typedef void (wxEvtHandler::*wxGridEditorCreatedEventFunction)(wxGridEditorCreat
#define EVT_GRID_EDITOR_HIDDEN(fn) EVT_GRID_CMD_EDITOR_HIDDEN(wxID_ANY, fn) #define EVT_GRID_EDITOR_HIDDEN(fn) EVT_GRID_CMD_EDITOR_HIDDEN(wxID_ANY, fn)
#define EVT_GRID_EDITOR_CREATED(fn) EVT_GRID_CMD_EDITOR_CREATED(wxID_ANY, fn) #define EVT_GRID_EDITOR_CREATED(fn) EVT_GRID_CMD_EDITOR_CREATED(wxID_ANY, fn)
#define EVT_GRID_CELL_BEGIN_DRAG(fn) EVT_GRID_CMD_CELL_BEGIN_DRAG(wxID_ANY, fn) #define EVT_GRID_CELL_BEGIN_DRAG(fn) EVT_GRID_CMD_CELL_BEGIN_DRAG(wxID_ANY, fn)
#define EVT_GRID_TABBING(fn) EVT_GRID_CMD_TABBING(wxID_ANY, fn)
// we used to have a single wxEVT_GRID_CELL_CHANGE event but it was split into // we used to have a single wxEVT_GRID_CELL_CHANGE event but it was split into
// wxEVT_GRID_CELL_CHANGING and CHANGED ones in wx 2.9.0, however the CHANGED // wxEVT_GRID_CELL_CHANGING and CHANGED ones in wx 2.9.0, however the CHANGED

View File

@@ -1894,6 +1894,29 @@ public:
wxGRID_DRAW_BOX_RECT wxGRID_DRAW_BOX_RECT
}; };
/**
Constants defining different support built-in TAB handling behaviours.
The elements of this enum determine what happens when TAB is pressed
when the cursor is in the rightmost column (or Shift-TAB is pressed
when the cursor is in the leftmost one).
@see SetTabBehaviour(), @c wxEVT_GRID_TABBING
@since 2.9.5
*/
enum TabBehaviour
{
/// Do nothing, this is default.
Tab_Stop,
/// Move to the beginning of the next (or the end of the previous) row.
Tab_Wrap,
/// Move to the next (or the previous) control after the grid.
Tab_Leave
};
/** /**
@name Constructors and Initialization @name Constructors and Initialization
*/ */
@@ -3469,6 +3492,25 @@ public:
*/ */
void SetGridCursor(const wxGridCellCoords& coords); void SetGridCursor(const wxGridCellCoords& coords);
/**
Set the grid's behaviour when the user presses the TAB key.
Pressing the TAB key moves the grid cursor right in the current row, if
there is a cell at the right and, similarly, Shift-TAB moves the cursor
to the left in the current row if it's not in the first column.
What happens if the cursor can't be moved because it it's already at
the beginning or end of the row can be configured using this function,
see wxGrid::TabBehaviour documentation for the detailed description.
IF none of the standard behaviours is appropriate, you can always
handle @c wxEVT_GRID_TABBING event directly to implement a custom
TAB-handling logic.
@since 2.9.5
*/
void SetTabBehaviour(TabBehaviour behaviour);
//@} //@}
@@ -4462,6 +4504,12 @@ public:
and updates the column to indicate the new sort order and refreshes and updates the column to indicate the new sort order and refreshes
itself. itself.
This event macro corresponds to @c wxEVT_GRID_COL_SORT event type. This event macro corresponds to @c wxEVT_GRID_COL_SORT event type.
@event{EVT_GRID_TABBING(func)}
This event is generated when the user presses TAB or Shift-TAB in the
grid. It can be used to customize the simple default TAB handling
logic, e.g. to go to the next non-empty cell instead of just the next
cell. See also wxGrid::SetTabBehaviour(). This event is new since
wxWidgets 2.9.5.
@endEventTable @endEventTable
@library{wxadv} @library{wxadv}

View File

@@ -160,6 +160,8 @@ BEGIN_EVENT_TABLE( GridFrame, wxFrame )
EVT_MENU( ID_COLNATIVEHEADER, GridFrame::SetNativeColHeader ) EVT_MENU( ID_COLNATIVEHEADER, GridFrame::SetNativeColHeader )
EVT_MENU( ID_COLDEFAULTHEADER, GridFrame::SetDefaultColHeader ) EVT_MENU( ID_COLDEFAULTHEADER, GridFrame::SetDefaultColHeader )
EVT_MENU( ID_COLCUSTOMHEADER, GridFrame::SetCustomColHeader ) EVT_MENU( ID_COLCUSTOMHEADER, GridFrame::SetCustomColHeader )
EVT_MENU_RANGE( ID_TAB_STOP, ID_TAB_LEAVE, GridFrame::SetTabBehaviour )
EVT_MENU( ID_TAB_CUSTOM, GridFrame::SetTabCustomHandler )
EVT_MENU( ID_TOGGLEGRIDLINES, GridFrame::ToggleGridLines ) EVT_MENU( ID_TOGGLEGRIDLINES, GridFrame::ToggleGridLines )
EVT_MENU( ID_AUTOSIZECOLS, GridFrame::AutoSizeCols ) EVT_MENU( ID_AUTOSIZECOLS, GridFrame::AutoSizeCols )
EVT_MENU( ID_CELLOVERFLOW, GridFrame::CellOverflow ) EVT_MENU( ID_CELLOVERFLOW, GridFrame::CellOverflow )
@@ -320,6 +322,12 @@ GridFrame::GridFrame()
colHeaderMenu->AppendRadioItem( ID_COLNATIVEHEADER, wxT("&Native") ); colHeaderMenu->AppendRadioItem( ID_COLNATIVEHEADER, wxT("&Native") );
colHeaderMenu->AppendRadioItem( ID_COLCUSTOMHEADER, wxT("&Custom") ); colHeaderMenu->AppendRadioItem( ID_COLCUSTOMHEADER, wxT("&Custom") );
wxMenu *tabBehaviourMenu = new wxMenu;
tabBehaviourMenu->AppendRadioItem(ID_TAB_STOP, "&Stop at the boundary");
tabBehaviourMenu->AppendRadioItem(ID_TAB_WRAP, "&Wrap at the boundary");
tabBehaviourMenu->AppendRadioItem(ID_TAB_LEAVE, "&Leave the grid");
tabBehaviourMenu->AppendRadioItem(ID_TAB_CUSTOM, "&Custom tab handler");
viewMenu->AppendSubMenu(tabBehaviourMenu, "&Tab behaviour");
wxMenu *colMenu = new wxMenu; wxMenu *colMenu = new wxMenu;
colMenu->Append( ID_SETLABELCOLOUR, wxT("Set &label colour...") ); colMenu->Append( ID_SETLABELCOLOUR, wxT("Set &label colour...") );
@@ -661,6 +669,42 @@ void GridFrame::SetDefaultColHeader( wxCommandEvent& WXUNUSED(ev) )
} }
void GridFrame::OnGridCustomTab(wxGridEvent& event)
{
// just for testing, make the cursor move up and down instead of the usual
// left and right
if ( event.ShiftDown() )
{
if ( grid->GetGridCursorRow() > 0 )
grid->MoveCursorUp( false );
}
else
{
if ( grid->GetGridCursorRow() < grid->GetNumberRows() - 1 )
grid->MoveCursorDown( false );
}
}
void GridFrame::SetTabBehaviour(wxCommandEvent& event)
{
// To make any built-in behaviour work, we need to disable the custom TAB
// handler, otherwise it would be overriding them.
grid->Disconnect(wxEVT_GRID_TABBING,
wxGridEventHandler(GridFrame::OnGridCustomTab));
grid->SetTabBehaviour(
static_cast<wxGrid::TabBehaviour>(event.GetId() - ID_TAB_STOP)
);
}
void GridFrame::SetTabCustomHandler(wxCommandEvent&)
{
grid->Connect(wxEVT_GRID_TABBING,
wxGridEventHandler(GridFrame::OnGridCustomTab),
NULL, this);
}
void GridFrame::ToggleGridLines( wxCommandEvent& WXUNUSED(ev) ) void GridFrame::ToggleGridLines( wxCommandEvent& WXUNUSED(ev) )
{ {
grid->EnableGridLines( grid->EnableGridLines(

View File

@@ -42,6 +42,8 @@ class GridFrame : public wxFrame
void SetNativeColHeader ( wxCommandEvent& ); void SetNativeColHeader ( wxCommandEvent& );
void SetCustomColHeader( wxCommandEvent& ); void SetCustomColHeader( wxCommandEvent& );
void SetDefaultColHeader( wxCommandEvent& ); void SetDefaultColHeader( wxCommandEvent& );
void SetTabBehaviour( wxCommandEvent& );
void SetTabCustomHandler( wxCommandEvent& );
void ToggleGridLines( wxCommandEvent& ); void ToggleGridLines( wxCommandEvent& );
void AutoSizeCols( wxCommandEvent& ); void AutoSizeCols( wxCommandEvent& );
void CellOverflow( wxCommandEvent& ); void CellOverflow( wxCommandEvent& );
@@ -102,6 +104,8 @@ class GridFrame : public wxFrame
void OnSetHighlightWidth(wxCommandEvent&); void OnSetHighlightWidth(wxCommandEvent&);
void OnSetROHighlightWidth(wxCommandEvent&); void OnSetROHighlightWidth(wxCommandEvent&);
void OnGridCustomTab(wxGridEvent& event);
public: public:
GridFrame(); GridFrame();
~GridFrame(); ~GridFrame();
@@ -140,6 +144,10 @@ public:
ID_COLDEFAULTHEADER, ID_COLDEFAULTHEADER,
ID_COLNATIVEHEADER, ID_COLNATIVEHEADER,
ID_COLCUSTOMHEADER, ID_COLCUSTOMHEADER,
ID_TAB_STOP,
ID_TAB_WRAP,
ID_TAB_LEAVE,
ID_TAB_CUSTOM,
ID_GRIDLINECOLOUR, ID_GRIDLINECOLOUR,
ID_INSERTROW, ID_INSERTROW,
ID_INSERTCOL, ID_INSERTCOL,

View File

@@ -155,6 +155,7 @@ wxDEFINE_EVENT( wxEVT_GRID_SELECT_CELL, wxGridEvent );
wxDEFINE_EVENT( wxEVT_GRID_EDITOR_SHOWN, wxGridEvent ); wxDEFINE_EVENT( wxEVT_GRID_EDITOR_SHOWN, wxGridEvent );
wxDEFINE_EVENT( wxEVT_GRID_EDITOR_HIDDEN, wxGridEvent ); wxDEFINE_EVENT( wxEVT_GRID_EDITOR_HIDDEN, wxGridEvent );
wxDEFINE_EVENT( wxEVT_GRID_EDITOR_CREATED, wxGridEditorCreatedEvent ); wxDEFINE_EVENT( wxEVT_GRID_EDITOR_CREATED, wxGridEditorCreatedEvent );
wxDEFINE_EVENT( wxEVT_GRID_TABBING, wxGridEvent );
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// private helpers // private helpers
@@ -2514,6 +2515,8 @@ void wxGrid::Init()
// now anyhow, so just set the parameters directly // now anyhow, so just set the parameters directly
m_xScrollPixelsPerLine = GRID_SCROLL_LINE_X; m_xScrollPixelsPerLine = GRID_SCROLL_LINE_X;
m_yScrollPixelsPerLine = GRID_SCROLL_LINE_Y; m_yScrollPixelsPerLine = GRID_SCROLL_LINE_Y;
m_tabBehaviour = Tab_Stop;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -4933,30 +4936,18 @@ void wxGrid::OnKeyDown( wxKeyEvent& event )
break; break;
case WXK_TAB: case WXK_TAB:
if (event.ShiftDown())
{ {
if ( GetGridCursorCol() > 0 ) // send an event to the grid's parents for custom handling
wxGridEvent gridEvt(GetId(), wxEVT_GRID_TABBING, this,
GetGridCursorRow(), GetGridCursorCol(),
-1, -1, false, event);
if ( ProcessWindowEvent(gridEvt) )
{ {
MoveCursorLeft( false ); // the event has been handled so no need for more processing
} break;
else
{
// at left of grid
DisableCellEditControl();
}
}
else
{
if ( GetGridCursorCol() < GetNumberCols() - 1 )
{
MoveCursorRight( false );
}
else
{
// at right of grid
DisableCellEditControl();
} }
} }
DoGridProcessTab( event );
break; break;
case WXK_HOME: case WXK_HOME:
@@ -5088,6 +5079,69 @@ void wxGrid::OnEraseBackground(wxEraseEvent&)
{ {
} }
void wxGrid::DoGridProcessTab(wxKeyboardState& kbdState)
{
const bool isForwardTab = !kbdState.ShiftDown();
// TAB processing only changes when we are at the borders of the grid, so
// let's first handle the common behaviour when we are not at the border.
if ( isForwardTab )
{
if ( GetGridCursorCol() < GetNumberCols() - 1 )
{
MoveCursorRight( false );
return;
}
}
else // going back
{
if ( GetGridCursorCol() )
{
MoveCursorLeft( false );
return;
}
}
// We only get here if the cursor is at the border of the grid, apply the
// configured behaviour.
switch ( m_tabBehaviour )
{
case Tab_Stop:
// Nothing special to do, we remain at the current cell.
break;
case Tab_Wrap:
// Go to the beginning of the next or the end of the previous row.
if ( isForwardTab )
{
if ( GetGridCursorRow() < GetNumberRows() - 1 )
{
GoToCell( GetGridCursorRow() + 1, 0 );
return;
}
}
else
{
if ( GetGridCursorRow() > 0 )
{
GoToCell( GetGridCursorRow() - 1, GetNumberCols() - 1 );
return;
}
}
break;
case Tab_Leave:
if ( Navigate( isForwardTab ? wxNavigationKeyEvent::IsForward
: wxNavigationKeyEvent::IsBackward ) )
return;
break;
}
// If we remain in this cell, stop editing it if we were doing so.
DisableCellEditControl();
}
bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
{ {
if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 ) if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 )