Improve usability of wxGrid actions using mouse dragging

Implement auto scrolling and handle ESCAPE to cancel the actions done by
dragging the mouse, such as resizing or selecting an area.

Closes #22292.
This commit is contained in:
DietmarSchwertberger
2022-04-09 21:45:59 +02:00
committed by Vadim Zeitlin
parent 8245ba96ee
commit 1b158caa83
5 changed files with 220 additions and 19 deletions

View File

@@ -13,7 +13,6 @@
- Make Begin/EndBatch() the same as the generic Freeze/Thaw()
- Review the column reordering code, it's a mess.
- Implement row reordering after dealing with the columns.
*/
// For compilers that support precompilation, includes "wx/wx.h".
@@ -3034,8 +3033,11 @@ void wxGrid::Init()
m_dragMoveCol = -1;
m_dragLastPos = -1;
m_dragRowOrCol = -1;
m_dragRowOrColOldSize = -1;
m_isDragging = false;
m_cancelledDragging = false;
m_startDragPos = wxDefaultPosition;
m_lastMousePos = wxDefaultPosition;
m_sortCol = wxNOT_FOUND;
m_sortIsAscending = true;
@@ -3864,12 +3866,99 @@ void wxGrid::PrepareDCFor(wxDC &dc, wxGridWindow *gridWindow)
dc.SetDeviceOrigin(dcOrigin.x, dcOrigin.y);
}
void wxGrid::CheckDoDragScroll(wxGridSubwindow *eventGridWindow, wxGridSubwindow *gridWindow,
wxPoint posEvent, int direction)
{
// helper for Process{Row|Col}LabelMouseEvent, ProcessGridCellMouseEvent:
// scroll when at the edges or outside the window w. respect to direction
// eventGridWindow: the window that received the mouse event
// gridWindow: the same or the corresponding non-frozen window
if ( !m_isDragging )
{
// drag is just starting
m_lastMousePos = posEvent;
return;
}
int w, h;
eventGridWindow->GetSize(&w, &h);
// ViewStart is scroll position in scroll units
wxPoint scrollPos = GetViewStart();
wxPoint newScrollPos = wxPoint(wxDefaultCoord, wxDefaultCoord);
if ( direction & wxHORIZONTAL )
{
// check x direction
if ( eventGridWindow->IsFrozen() && posEvent.x < w )
{
// in the frozen window, moving left?
if ( scrollPos.x > 0 && posEvent.x < m_lastMousePos.x )
newScrollPos.x = scrollPos.x - 1;
}
else if ( eventGridWindow->IsFrozen() && posEvent.x >= w )
{
// frozen window was left, add the width of the non-frozen window
w += gridWindow->GetSize().x;
}
if ( posEvent.x < 0 && scrollPos.x > 0 )
newScrollPos.x = scrollPos.x - 1;
else if ( posEvent.x >= w )
newScrollPos.x = scrollPos.x + 1;
}
if ( direction & wxVERTICAL )
{
// check y direction
if ( eventGridWindow->IsFrozen() && posEvent.y < h )
{
// in the frozen window, moving upward?
if ( scrollPos.y && posEvent.y < m_lastMousePos.y )
newScrollPos.y = scrollPos.y - 1;
}
else if ( eventGridWindow->IsFrozen() && posEvent.y >= h )
{
// frozen window was left, add the height of the non-frozen window
h += gridWindow->GetSize().y;
}
if ( posEvent.y < 0 && scrollPos.y > 0 )
newScrollPos.y = scrollPos.y - 1;
else if ( posEvent.y >= h )
newScrollPos.y = scrollPos.y + 1;
}
if ( newScrollPos.x != wxDefaultCoord || newScrollPos.y != wxDefaultCoord )
Scroll(newScrollPos);
m_lastMousePos = posEvent;
}
bool wxGrid::CheckIfDragCancelled(wxMouseEvent *event)
{
// helper for Process{Row|Col}LabelMouseEvent, ProcessGridCellMouseEvent:
// block re-triggering m_isDragging
if ( !m_cancelledDragging )
return false;
if ( event->LeftIsDown() )
return true;
m_cancelledDragging = false;
return false;
}
void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindow* rowLabelWin )
{
int y;
wxGridWindow *gridWindow = rowLabelWin->IsFrozen() ? m_frozenRowGridWin : m_gridWin;
event.SetPosition(event.GetPosition() + GetGridWindowOffset(gridWindow));
// store position, before it's modified in the next step
const wxPoint posEvent = event.GetPosition();
event.SetPosition(posEvent + GetGridWindowOffset(gridWindow));
// for drag, we could be moving from the window sending the event to the other
if ( rowLabelWin->IsFrozen() && event.GetPosition().y > rowLabelWin->GetClientSize().y )
@@ -3878,6 +3967,15 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo
CalcGridWindowUnscrolledPosition(0, event.GetPosition().y, NULL, &y, gridWindow);
int row = YToRow( y );
if ( CheckIfDragCancelled(&event) )
return;
if ( event.Dragging() && (m_winCapture == rowLabelWin) )
{
// scroll when at the edges or outside the window
CheckDoDragScroll(rowLabelWin, m_rowLabelWin, posEvent, wxVERTICAL);
}
if ( event.Dragging() )
{
if (!m_isDragging)
@@ -3995,7 +4093,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo
row = YToEdgeOfRow(y);
if ( row != wxNOT_FOUND && CanDragRowSize(row) )
{
DoStartResizeRowOrCol(row);
DoStartResizeRowOrCol(row, GetRowSize(row));
ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, rowLabelWin);
}
else // not a request to start resizing
@@ -4157,6 +4255,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo
ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, rowLabelWin);
m_dragLastPos = -1;
m_lastMousePos = wxDefaultPosition;
m_isDragging = false;
}
@@ -4311,13 +4410,14 @@ void wxGrid::DoColHeaderClick(int col)
}
}
void wxGrid::DoStartResizeRowOrCol(int col)
void wxGrid::DoStartResizeRowOrCol(int col, int size)
{
// Hide the editor if it's currently shown to avoid any weird interactions
// with it while dragging the row/column separator.
AcceptCellEditControlIfShown();
m_dragRowOrCol = col;
m_dragRowOrColOldSize = size;
}
void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindow* colLabelWin )
@@ -4325,7 +4425,10 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
int x;
wxGridWindow *gridWindow = colLabelWin->IsFrozen() ? m_frozenColGridWin : m_gridWin;
event.SetPosition(event.GetPosition() + GetGridWindowOffset(gridWindow));
// store position, before it's modified in the next step
const wxPoint posEvent = event.GetPosition();
event.SetPosition(posEvent + GetGridWindowOffset(gridWindow));
// for drag, we could be moving from the window sending the event to the other
if (colLabelWin->IsFrozen() && event.GetPosition().x > colLabelWin->GetClientSize().x)
@@ -4334,6 +4437,16 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
CalcGridWindowUnscrolledPosition(event.GetPosition().x, 0, &x, NULL, gridWindow);
int col = XToCol(x);
if ( CheckIfDragCancelled(&event) )
return;
if ( event.Dragging() && (m_winCapture == colLabelWin) )
{
// scroll when at the edges or outside the window
CheckDoDragScroll(colLabelWin, GetColLabelWindow(), posEvent, wxHORIZONTAL);
}
if ( event.Dragging() )
{
if (!m_isDragging)
@@ -4452,7 +4565,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
int colEdge = XToEdgeOfCol(x);
if ( colEdge != wxNOT_FOUND && CanDragColSize(colEdge) )
{
DoStartResizeRowOrCol(colEdge);
DoStartResizeRowOrCol(colEdge, GetColSize(colEdge));
ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, colLabelWin);
}
else // not a request to start resizing
@@ -4611,6 +4724,7 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo
ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
m_dragLastPos = -1;
m_lastMousePos = wxDefaultPosition;
m_isDragging = false;
}
@@ -4736,6 +4850,7 @@ void wxGrid::DoAfterDraggingEnd()
m_isDragging = false;
m_startDragPos = wxDefaultPosition;
m_lastMousePos = wxDefaultPosition;
m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
m_winCapture->SetCursor( *wxSTANDARD_CURSOR );
@@ -4803,9 +4918,6 @@ void wxGrid::ChangeCursorMode(CursorMode mode,
case WXGRID_CURSOR_MOVE_ROW:
case WXGRID_CURSOR_MOVE_COL:
// Currently we don't capture mouse when moving columns, which is
// almost certainly wrong.
captureMouse = false;
win->SetCursor( wxCursor(wxCURSOR_HAND) );
break;
@@ -4931,12 +5043,16 @@ wxGrid::DoGridCellLeftDown(wxMouseEvent& event,
{
int dragRowOrCol = wxNOT_FOUND;
if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
{
dragRowOrCol = XToEdgeOfCol(pos.x);
DoStartResizeRowOrCol(dragRowOrCol, GetColSize(dragRowOrCol));
}
else
{
dragRowOrCol = YToEdgeOfRow(pos.y);
DoStartResizeRowOrCol(dragRowOrCol, GetRowSize(dragRowOrCol));
}
wxCHECK_RET( dragRowOrCol != -1, "Can't determine row or column in resizing mode" );
DoStartResizeRowOrCol(dragRowOrCol);
}
break;
@@ -5115,13 +5231,20 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow *eventG
// the window receiving the event might not be the same as the one under
// the mouse (e.g. in the case of a dragging event started in one window,
// but continuing over another one)
if ( CheckIfDragCancelled(&event) )
return;
wxGridWindow *gridWindow =
DevicePosToGridWindow(event.GetPosition() + eventGridWindow->GetPosition());
if ( !gridWindow )
gridWindow = eventGridWindow;
event.SetPosition(event.GetPosition() + eventGridWindow->GetPosition() -
// store position, before it's modified in the next step
const wxPoint posEvent = event.GetPosition();
event.SetPosition(posEvent + eventGridWindow->GetPosition() -
wxPoint(m_rowLabelWidth, m_colLabelHeight));
wxPoint pos = CalcGridWindowUnscrolledPosition(event.GetPosition(), gridWindow);
@@ -5152,6 +5275,13 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow *eventG
const bool isDraggingWithLeft = event.Dragging() && event.LeftIsDown();
if ( isDraggingWithLeft && (m_winCapture == eventGridWindow) )
{
// scroll when at the edges or outside the window
CheckDoDragScroll(eventGridWindow, m_gridWin, posEvent,
wxHORIZONTAL | wxVERTICAL);
}
// 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.
@@ -5295,7 +5425,7 @@ void wxGrid::DoEndDragResizeCol(const wxMouseEvent& event, wxGridWindow* gridWin
void wxGrid::DoHeaderStartDragResizeCol(int col)
{
DoStartResizeRowOrCol(col);
DoStartResizeRowOrCol(col, GetColSize(col));
}
void wxGrid::DoHeaderDragResizeCol(int width)
@@ -6185,7 +6315,44 @@ void wxGrid::OnKeyDown( wxKeyEvent& event )
break;
case WXK_ESCAPE:
ClearSelection();
if ( m_isDragging && m_winCapture )
{
switch ( m_cursorMode )
{
case WXGRID_CURSOR_MOVE_COL:
case WXGRID_CURSOR_MOVE_ROW:
// end row/column moving
m_winCapture->Refresh();
m_dragLastPos = -1;
break;
case WXGRID_CURSOR_RESIZE_ROW:
case WXGRID_CURSOR_RESIZE_COL:
// reset to size from before dragging
(m_cursorMode == WXGRID_CURSOR_RESIZE_ROW
? static_cast<const wxGridOperations&>(wxGridRowOperations())
: static_cast<const wxGridOperations&>(wxGridColumnOperations())
).SetLineSize(this, m_dragRowOrCol, m_dragRowOrColOldSize);
m_dragRowOrCol = -1;
break;
case WXGRID_CURSOR_SELECT_CELL:
case WXGRID_CURSOR_SELECT_ROW:
case WXGRID_CURSOR_SELECT_COL:
if ( m_selection )
m_selection->CancelSelecting();
break;
}
EndDraggingIfNecessary();
// ensure that a new drag operation is only started after a LeftUp
m_cancelledDragging = true;
}
else
{
ClearSelection();
}
break;
case WXK_TAB: