Tuesday, January 17, 2012

wxGrid editors and the arrow keys

When editing a wxGrid cell the standard behaviour is not very user-friendly: if you press one of the arrow keys you can move the caret in the editor, but you cannot select another cell. For example, a user would expect that pressing the up key would close the cell editor and move the selection to the cell above, but nothing happens.

It is possible to add this behaviour by handling the EVT_GRID_EDITOR_CREATED event for the grid. For example:

   EVT_GRID_EDITOR_CREATED( fsGridBase::OnEditorCreated )  

The OnEditorCreated() function simply sets an event handler for the EVT_KEY_DOWN event for the editor control:

 void fsGridBase::OnEditorCreated( wxGridEditorCreatedEvent& event )  
 {  
   event.GetControl()->Bind( wxEVT_KEY_DOWN, &fsGridBase::OnGridEditorKey, this );  
   event.Skip();  
 }  

The OnGridEditorKey() function can handle the EVT_KEY_DOWN event as desired by the developer. The basic rule is the following: call event.Skip() to send the event to the editor control, getting the usual behaviour.
Call GetEventHandler()->ProcessEvent(event) to send the event directly to the grid, bypassing the cell editor. This choice will let the arrow keys move to another cell.

One example could be the following:

 void fsGridBase::OnGridEditorKey( wxKeyEvent& event )  
 {  
   switch ( event.GetKeyCode() ) {  
     case WXK_DOWN:  
     case WXK_UP:  
       GetEventHandler()->ProcessEvent( event );  
       break;  
     case WXK_LEFT:  
       {  
         wxGridCellEditor* editor = GetCellEditor( GetGridCursorRow(), GetGridCursorCol() );  
         wxControl* ctrl = editor->GetControl();  
         if( ctrl->IsKindOf(CLASSINFO(wxTextCtrl)) ) {  
           wxTextCtrl* tp = dynamic_cast<wxTextCtrl*>( ctrl );  
           if( tp->GetInsertionPoint() == 0 ) {  
             // we are at the beginning of the text control so let's move to the previous column  
             GetEventHandler()->ProcessEvent( event );  
           }  
           else {  
             event.Skip();  
           }  
         }  
         editor->DecRef();  
       }  
       break;  
     case WXK_RIGHT:  
       {  
         wxGridCellEditor* editor = GetCellEditor( GetGridCursorRow(), GetGridCursorCol() );  
         wxControl* ctrl = editor->GetControl();  
         if( ctrl->IsKindOf(CLASSINFO(wxTextCtrl)) ) {  
           wxTextCtrl* tp = dynamic_cast<wxTextCtrl*>( ctrl );  
           if( tp->GetInsertionPoint() == tp->GetLastPosition() ) {  
             // we are at the end of the text control so let's move to the next column  
             GetEventHandler()->ProcessEvent( event );  
           }  
           else {  
             event.Skip();  
           }  
         }  
         editor->DecRef();  
       }  
       break;  
     default:  
       event.Skip();  
   }  
 }  

The up and down arrows will always move to another cell. The left and right arrows will move the caret in the control as usual, but if the caret is at the beginning or at the end of the control the key will move to another cell.

The example above is a good starting point, but it will cause problems if the grid uses wxGridCellChoiceEditors because that editor would not work well with keyboard keys. It will work well with the mouse, anyway.

No comments:

Post a Comment