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:
parent
8245ba96ee
commit
1b158caa83
@ -100,6 +100,7 @@ class WXDLLIMPEXP_FWD_CORE wxGridCornerLabelWindow;
|
||||
class WXDLLIMPEXP_FWD_CORE wxGridEvent;
|
||||
class WXDLLIMPEXP_FWD_CORE wxGridRowLabelWindow;
|
||||
class WXDLLIMPEXP_FWD_CORE wxGridWindow;
|
||||
class WXDLLIMPEXP_FWD_CORE wxGridSubwindow;
|
||||
class WXDLLIMPEXP_FWD_CORE wxGridTypeRegistry;
|
||||
class WXDLLIMPEXP_FWD_CORE wxGridSelection;
|
||||
|
||||
@ -2804,10 +2805,19 @@ protected:
|
||||
// or -1 if there is no resize operation in progress.
|
||||
int m_dragRowOrCol;
|
||||
|
||||
// Original row or column size when resizing; used when the user cancels
|
||||
int m_dragRowOrColOldSize;
|
||||
|
||||
// true if a drag operation is in progress; when this is true,
|
||||
// m_startDragPos is valid, i.e. not wxDefaultPosition
|
||||
bool m_isDragging;
|
||||
|
||||
// true if a drag operation was canceled
|
||||
// (mouse event Dragging() might still be active until LeftUp)
|
||||
// m_isDragging can only be set after m_cancelledDragging is cleared.
|
||||
// This is done when a mouse event happens with left button up.
|
||||
bool m_cancelledDragging;
|
||||
|
||||
// the position (in physical coordinates) where the user started dragging
|
||||
// the mouse or wxDefaultPosition if mouse isn't being dragged
|
||||
//
|
||||
@ -2816,6 +2826,10 @@ protected:
|
||||
// setting m_isDragging to true
|
||||
wxPoint m_startDragPos;
|
||||
|
||||
// the position of the last mouse event
|
||||
// used for detection of the movement direction
|
||||
wxPoint m_lastMousePos;
|
||||
|
||||
bool m_waitForSlowClick;
|
||||
|
||||
wxCursor m_rowResizeCursor;
|
||||
@ -2960,6 +2974,13 @@ private:
|
||||
// release the mouse capture if it's currently captured
|
||||
void EndDraggingIfNecessary();
|
||||
|
||||
// helper for Process...MouseEvent to block re-triggering m_isDragging
|
||||
bool CheckIfDragCancelled(wxMouseEvent *event);
|
||||
|
||||
// helper for Process...MouseEvent to scroll
|
||||
void CheckDoDragScroll(wxGridSubwindow *eventGridWindow, wxGridSubwindow *gridWindow,
|
||||
wxPoint posEvent, int direction);
|
||||
|
||||
// return true if the grid should be refreshed right now
|
||||
bool ShouldRefresh() const
|
||||
{
|
||||
@ -3029,7 +3050,7 @@ private:
|
||||
|
||||
void DoColHeaderClick(int col);
|
||||
|
||||
void DoStartResizeRowOrCol(int col);
|
||||
void DoStartResizeRowOrCol(int col, int size);
|
||||
void DoStartMoveRow(int col);
|
||||
void DoStartMoveCol(int col);
|
||||
|
||||
|
@ -112,6 +112,7 @@ public:
|
||||
wxVectorGridBlockCoords& GetBlocks() { return m_selection; }
|
||||
|
||||
void EndSelecting();
|
||||
void CancelSelecting();
|
||||
|
||||
private:
|
||||
void SelectBlockNoEvent(const wxGridBlockCoords& block)
|
||||
|
@ -281,6 +281,8 @@ public:
|
||||
|
||||
wxGrid *GetOwner() { return m_owner; }
|
||||
|
||||
virtual bool IsFrozen() const { return false; }
|
||||
|
||||
protected:
|
||||
void OnMouseCaptureLost(wxMouseCaptureLostEvent& event);
|
||||
|
||||
@ -298,8 +300,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool IsFrozen() const { return false; }
|
||||
|
||||
private:
|
||||
void OnPaint( wxPaintEvent& event );
|
||||
void OnMouseEvent( wxMouseEvent& event );
|
||||
@ -330,8 +330,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool IsFrozen() const { return false; }
|
||||
|
||||
private:
|
||||
void OnPaint( wxPaintEvent& event );
|
||||
void OnMouseEvent( wxMouseEvent& event );
|
||||
|
@ -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:
|
||||
|
@ -72,6 +72,20 @@ void wxGridSelection::EndSelecting()
|
||||
m_grid->GetEventHandler()->ProcessEvent(gridEvt);
|
||||
}
|
||||
|
||||
void wxGridSelection::CancelSelecting()
|
||||
{
|
||||
// It's possible that nothing was selected finally, e.g. the mouse could
|
||||
// have been dragged around only to return to the starting cell, just don't
|
||||
// do anything in this case.
|
||||
if ( !IsSelection() )
|
||||
return;
|
||||
|
||||
const wxGridBlockCoords& block = m_selection.back();
|
||||
m_grid->RefreshBlock(block.GetTopLeft(), block.GetBottomRight());
|
||||
m_selection.pop_back();
|
||||
}
|
||||
|
||||
|
||||
bool wxGridSelection::IsInSelection( int row, int col ) const
|
||||
{
|
||||
// Check whether the given cell is contained in one of the selected blocks.
|
||||
|
Loading…
Reference in New Issue
Block a user