Fix mouse event handling while dragging in wxGrid

Ignore all the mouse events other than "left up" while dragging to avoid
releasing the mouse and ending the dragging operation too soon.

This required non-trivial refactoring of the code which hopefully should
also make it slightly more clear by centralizing high level logic in
ProcessGridCellMouseEvent() itself and calling various helper functions
from it instead of spreading this logic over the entire call tree.

The code still remains pretty confusing and rewriting it to use
wxMouseEventsManager (which would need to be generalized first to become
a template class using arbitrary item type instead of just "int", as
now) would undoubtedly do it a lot of good.

Closes #18186.
This commit is contained in:
Vadim Zeitlin
2018-08-17 00:57:04 +02:00
parent 53972b17ee
commit bb00501e77
2 changed files with 82 additions and 67 deletions

View File

@@ -2218,10 +2218,14 @@ private:
// SetColPos() and ResetColPos()) // SetColPos() and ResetColPos())
void RefreshAfterColPosChange(); void RefreshAfterColPosChange();
// reset the variables used during dragging operations after it ended // reset the variables used during dragging operations after it ended,
// because we lost mouse capture // either because we called EndDraggingIfNecessary() ourselves or because
// we lost mouse capture
void DoAfterDraggingEnd(); void DoAfterDraggingEnd();
// release the mouse capture if it's currently captured
void EndDraggingIfNecessary();
// return the position (not index) of the column at the given logical pixel // return the position (not index) of the column at the given logical pixel
// position // position
@@ -2242,8 +2246,12 @@ private:
// process row/column resizing drag event // process row/column resizing drag event
void DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper); void DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper);
// process mouse drag event in the grid window // process mouse drag event in the grid window, return false if starting
void DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords); // dragging was vetoed by the user-defined wxEVT_GRID_CELL_BEGIN_DRAG
// handler
bool DoGridDragEvent(wxMouseEvent& event,
const wxGridCellCoords& coords,
bool isFirstDrag);
// process different clicks on grid cells // process different clicks on grid cells
void DoGridCellLeftDown(wxMouseEvent& event, void DoGridCellLeftDown(wxMouseEvent& event,

View File

@@ -3863,6 +3863,16 @@ void wxGrid::DoAfterDraggingEnd()
m_winCapture = NULL; m_winCapture = NULL;
} }
void wxGrid::EndDraggingIfNecessary()
{
if ( m_winCapture )
{
m_winCapture->ReleaseMouse();
DoAfterDraggingEnd();
}
}
void wxGrid::ChangeCursorMode(CursorMode mode, void wxGrid::ChangeCursorMode(CursorMode mode,
wxWindow *win, wxWindow *win,
bool captureMouse) bool captureMouse)
@@ -3897,11 +3907,7 @@ void wxGrid::ChangeCursorMode(CursorMode mode,
win = m_gridWin; win = m_gridWin;
} }
if ( m_winCapture ) EndDraggingIfNecessary();
{
m_winCapture->ReleaseMouse();
m_winCapture = NULL;
}
m_cursorMode = mode; m_cursorMode = mode;
@@ -4019,34 +4025,14 @@ void wxGrid::DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper)
oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos); oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos);
} }
void wxGrid::DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords) bool wxGrid::DoGridDragEvent(wxMouseEvent& event,
const wxGridCellCoords& coords,
bool isFirstDrag)
{ {
if ( !m_isDragging )
{
// Don't start doing anything until the mouse has been dragged far
// enough
const wxPoint& pt = event.GetPosition();
if ( m_startDragPos == wxDefaultPosition )
{
m_startDragPos = pt;
return;
}
if ( abs(m_startDragPos.x - pt.x) <= DRAG_SENSITIVITY &&
abs(m_startDragPos.y - pt.y) <= DRAG_SENSITIVITY )
return;
}
const bool isFirstDrag = !m_isDragging;
m_isDragging = true;
switch ( m_cursorMode ) switch ( m_cursorMode )
{ {
case WXGRID_CURSOR_SELECT_CELL: case WXGRID_CURSOR_SELECT_CELL:
// no further handling if handled by user return DoGridCellDrag(event, coords, isFirstDrag);
if ( DoGridCellDrag(event, coords, isFirstDrag) == false )
return;
break;
case WXGRID_CURSOR_RESIZE_ROW: case WXGRID_CURSOR_RESIZE_ROW:
DoGridLineDrag(event, wxGridRowOperations()); DoGridLineDrag(event, wxGridRowOperations());
@@ -4060,13 +4046,7 @@ void wxGrid::DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords
event.Skip(); event.Skip();
} }
if ( isFirstDrag ) return true;
{
wxASSERT_MSG( !m_winCapture, "shouldn't capture the mouse twice" );
m_winCapture = m_gridWin;
m_winCapture->CaptureMouse();
}
} }
void void
@@ -4160,12 +4140,6 @@ wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords)
{ {
if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
{ {
if (m_winCapture)
{
m_winCapture->ReleaseMouse();
m_winCapture = NULL;
}
if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() ) if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() )
{ {
ClearSelection(); ClearSelection();
@@ -4266,14 +4240,6 @@ wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event),
void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event) void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event)
{ {
if ( event.Entering() || event.Leaving() )
{
// we don't care about these events but we must not reset m_isDragging
// if they happen so return before anything else is done
event.Skip();
return;
}
const wxPoint pos = CalcUnscrolledPosition(event.GetPosition()); const wxPoint pos = CalcUnscrolledPosition(event.GetPosition());
// coordinates of the cell under mouse // coordinates of the cell under mouse
@@ -4287,17 +4253,64 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event)
coords.SetCol(coords.GetCol() + cell_cols); coords.SetCol(coords.GetCol() + cell_cols);
} }
if ( event.Dragging() ) // Releasing the left mouse button must be processed in any case, so deal
// with it first.
if ( event.LeftUp() )
{ {
if ( event.LeftIsDown() ) // Note that we must call this one first, before resetting the
DoGridDragEvent(event, coords); // drag-related data, as it relies on m_cursorMode being still set and
else // EndDraggingIfNecessary() resets it.
event.Skip(); DoGridCellLeftUp(event, coords);
EndDraggingIfNecessary();
return; return;
} }
m_isDragging = false; const bool isDraggingWithLeft = event.Dragging() && event.LeftIsDown();
m_startDragPos = wxDefaultPosition;
// While dragging the mouse, only releasing the left mouse button, which
// cancels the drag operation, is processed (above) and any other events
// are just ignored while it's in progress.
if ( m_isDragging )
{
if ( isDraggingWithLeft )
DoGridDragEvent(event, coords, false /* not first drag */);
return;
}
// Now check if we're starting a drag operation (if it had been already
// started, m_isDragging would be true above).
if ( isDraggingWithLeft )
{
// To avoid accidental drags, don't start doing anything until the
// mouse has been dragged far enough.
const wxPoint& pt = event.GetPosition();
if ( m_startDragPos == wxDefaultPosition )
{
m_startDragPos = pt;
return;
}
if ( abs(m_startDragPos.x - pt.x) <= DRAG_SENSITIVITY &&
abs(m_startDragPos.y - pt.y) <= DRAG_SENSITIVITY )
return;
if ( DoGridDragEvent(event, coords, true /* first drag */) )
{
wxASSERT_MSG( !m_winCapture, "shouldn't capture the mouse twice" );
m_winCapture = m_gridWin;
m_winCapture->CaptureMouse();
m_isDragging = true;
}
return;
}
// If we're not dragging, cancel any dragging operation which could have
// been in progress.
EndDraggingIfNecessary();
// deal with various button presses // deal with various button presses
if ( event.IsButton() ) if ( event.IsButton() )
@@ -4315,12 +4328,6 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event)
else if ( event.RightDClick() ) else if ( event.RightDClick() )
SendEvent(wxEVT_GRID_CELL_RIGHT_DCLICK, coords, event); SendEvent(wxEVT_GRID_CELL_RIGHT_DCLICK, coords, event);
} }
// this one should be called even if we're not over any cell
if ( event.LeftUp() )
{
DoGridCellLeftUp(event, coords);
}
} }
else if ( event.Moving() ) else if ( event.Moving() )
{ {