From: Vadim Zeitlin Date: Sat, 13 Oct 2012 22:55:18 +0000 (+0000) Subject: Make TAB behaviour in wxGrid more configurable. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/1dc17bcafb89b0bde0406e692df79111c197d38e 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 --- diff --git a/docs/changes.txt b/docs/changes.txt index de8db9d89c..a5cb4aca19 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -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: diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h index 9f3dd4e63d..6ddc69b43a 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h @@ -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 diff --git a/interface/wx/grid.h b/interface/wx/grid.h index dc25dbd3fd..de9493045b 100644 --- a/interface/wx/grid.h +++ b/interface/wx/grid.h @@ -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} diff --git a/samples/grid/griddemo.cpp b/samples/grid/griddemo.cpp index 2b3ba6df60..dc0f993dfa 100644 --- a/samples/grid/griddemo.cpp +++ b/samples/grid/griddemo.cpp @@ -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(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( diff --git a/samples/grid/griddemo.h b/samples/grid/griddemo.h index 401553a2ba..8805614194 100644 --- a/samples/grid/griddemo.h +++ b/samples/grid/griddemo.h @@ -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, diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 6d6649d96e..ca15ddc2cc 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -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 ) - { - MoveCursorLeft( false ); - } - else - { - // at left of grid - DisableCellEditControl(); - } - } - else - { - if ( GetGridCursorCol() < GetNumberCols() - 1 ) - { - MoveCursorRight( false ); - } - else + // 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) ) { - // 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 )