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:
@@ -567,6 +567,7 @@ All (GUI):
|
||||
- Add support for wxALWAYS_SHOW_SB style to wxScrolled<> (Catalin Raceanu).
|
||||
- Add wxTreeCtrl::EnableBellOnNoMatch() (Jonathan Dagresta).
|
||||
- Implement incremental search in wxGenericListCtrl (Jonathan Dagresta).
|
||||
- Make TAB behaviour in wxGrid more flexible (Fulvio Senore).
|
||||
|
||||
wxGTK:
|
||||
|
||||
|
@@ -918,6 +918,15 @@ public:
|
||||
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
|
||||
// ------------------------
|
||||
|
||||
@@ -1172,6 +1181,8 @@ public:
|
||||
bool MoveCursorLeftBlock( bool expandSelection );
|
||||
bool MoveCursorRightBlock( bool expandSelection );
|
||||
|
||||
void SetTabBehaviour(TabBehaviour behaviour) { m_tabBehaviour = behaviour; }
|
||||
|
||||
|
||||
// ------ label and gridline formatting
|
||||
//
|
||||
@@ -2074,6 +2085,8 @@ protected:
|
||||
bool m_editable; // applies to whole grid
|
||||
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 Create();
|
||||
void CreateColumnWindow();
|
||||
@@ -2241,6 +2254,8 @@ private:
|
||||
void DoEndDragResizeCol(const wxMouseEvent& event);
|
||||
void DoEndMoveCol(int pos);
|
||||
|
||||
// process a TAB keypress
|
||||
void DoGridProcessTab(wxKeyboardState& kbdState);
|
||||
|
||||
// common implementations of methods defined for both rows and columns
|
||||
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_COL_MOVE, 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::*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_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_TABBING(id, fn) wx__DECLARE_GRIDEVT(TABBING, id, fn)
|
||||
|
||||
// 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)
|
||||
@@ -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_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_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
|
||||
// wxEVT_GRID_CELL_CHANGING and CHANGED ones in wx 2.9.0, however the CHANGED
|
||||
|
@@ -1894,6 +1894,29 @@ public:
|
||||
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
|
||||
*/
|
||||
@@ -3469,6 +3492,25 @@ public:
|
||||
*/
|
||||
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
|
||||
itself.
|
||||
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
|
||||
|
||||
@library{wxadv}
|
||||
|
@@ -160,6 +160,8 @@ BEGIN_EVENT_TABLE( GridFrame, wxFrame )
|
||||
EVT_MENU( ID_COLNATIVEHEADER, GridFrame::SetNativeColHeader )
|
||||
EVT_MENU( ID_COLDEFAULTHEADER, GridFrame::SetDefaultColHeader )
|
||||
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_AUTOSIZECOLS, GridFrame::AutoSizeCols )
|
||||
EVT_MENU( ID_CELLOVERFLOW, GridFrame::CellOverflow )
|
||||
@@ -320,6 +322,12 @@ GridFrame::GridFrame()
|
||||
colHeaderMenu->AppendRadioItem( ID_COLNATIVEHEADER, wxT("&Native") );
|
||||
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;
|
||||
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) )
|
||||
{
|
||||
grid->EnableGridLines(
|
||||
|
@@ -42,6 +42,8 @@ class GridFrame : public wxFrame
|
||||
void SetNativeColHeader ( wxCommandEvent& );
|
||||
void SetCustomColHeader( wxCommandEvent& );
|
||||
void SetDefaultColHeader( wxCommandEvent& );
|
||||
void SetTabBehaviour( wxCommandEvent& );
|
||||
void SetTabCustomHandler( wxCommandEvent& );
|
||||
void ToggleGridLines( wxCommandEvent& );
|
||||
void AutoSizeCols( wxCommandEvent& );
|
||||
void CellOverflow( wxCommandEvent& );
|
||||
@@ -102,6 +104,8 @@ class GridFrame : public wxFrame
|
||||
void OnSetHighlightWidth(wxCommandEvent&);
|
||||
void OnSetROHighlightWidth(wxCommandEvent&);
|
||||
|
||||
void OnGridCustomTab(wxGridEvent& event);
|
||||
|
||||
public:
|
||||
GridFrame();
|
||||
~GridFrame();
|
||||
@@ -140,6 +144,10 @@ public:
|
||||
ID_COLDEFAULTHEADER,
|
||||
ID_COLNATIVEHEADER,
|
||||
ID_COLCUSTOMHEADER,
|
||||
ID_TAB_STOP,
|
||||
ID_TAB_WRAP,
|
||||
ID_TAB_LEAVE,
|
||||
ID_TAB_CUSTOM,
|
||||
ID_GRIDLINECOLOUR,
|
||||
ID_INSERTROW,
|
||||
ID_INSERTCOL,
|
||||
|
@@ -155,6 +155,7 @@ wxDEFINE_EVENT( wxEVT_GRID_SELECT_CELL, wxGridEvent );
|
||||
wxDEFINE_EVENT( wxEVT_GRID_EDITOR_SHOWN, wxGridEvent );
|
||||
wxDEFINE_EVENT( wxEVT_GRID_EDITOR_HIDDEN, wxGridEvent );
|
||||
wxDEFINE_EVENT( wxEVT_GRID_EDITOR_CREATED, wxGridEditorCreatedEvent );
|
||||
wxDEFINE_EVENT( wxEVT_GRID_TABBING, wxGridEvent );
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// private helpers
|
||||
@@ -2514,6 +2515,8 @@ void wxGrid::Init()
|
||||
// now anyhow, so just set the parameters directly
|
||||
m_xScrollPixelsPerLine = GRID_SCROLL_LINE_X;
|
||||
m_yScrollPixelsPerLine = GRID_SCROLL_LINE_Y;
|
||||
|
||||
m_tabBehaviour = Tab_Stop;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -4933,30 +4936,18 @@ void wxGrid::OnKeyDown( wxKeyEvent& event )
|
||||
break;
|
||||
|
||||
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 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// at left of grid
|
||||
DisableCellEditControl();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( GetGridCursorCol() < GetNumberCols() - 1 )
|
||||
{
|
||||
MoveCursorRight( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
// at right of grid
|
||||
DisableCellEditControl();
|
||||
// the event has been handled so no need for more processing
|
||||
break;
|
||||
}
|
||||
}
|
||||
DoGridProcessTab( event );
|
||||
break;
|
||||
|
||||
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 )
|
||||
{
|
||||
if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 )
|
||||
|
Reference in New Issue
Block a user