]> git.saurik.com Git - wxWidgets.git/commitdiff
Make TAB behaviour in wxGrid more configurable.
authorVadim Zeitlin <vadim@wxwidgets.org>
Sat, 13 Oct 2012 22:55:18 +0000 (22:55 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sat, 13 Oct 2012 22:55:18 +0000 (22:55 +0000)
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

docs/changes.txt
include/wx/generic/grid.h
interface/wx/grid.h
samples/grid/griddemo.cpp
samples/grid/griddemo.h
src/generic/grid.cpp

index de8db9d89c9f48846376d4ca077e0ce5a36a3efe..a5cb4aca19da10883e9cb109b92ea8bc383b2ecc 100644 (file)
@@ -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:
 
index 9f3dd4e63d018820cc571c244de0cfa0e0d5d551..6ddc69b43a40dea67e56e1331fef45e2810907a9 100644 (file)
@@ -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
index dc25dbd3fd247891bc3aa480fbf56fb7b8949e0d..de9493045bea226f43876135eda9b74ff7e86c30 100644 (file)
@@ -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}
index 2b3ba6df6004fa7ac87352f0d3b775d41e177ed5..dc0f993dfa41cfb808f385b66e3ed46d74d6195f 100644 (file)
@@ -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(
index 401553a2ba081a2ffbf06c78a72367cd5967489b..88056141949637a69a0f97c8c95d911adc18441b 100644 (file)
@@ -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,
index 6d6649d96ea2b892061f4391f77cf5ca06720a1b..ca15ddc2cc325375152514a6aea135c09d2d873e 100644 (file)
@@ -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 )