wxSplitterWindow mouse capture improvements and cleanup.

- Handle mouse-capture-lost event to abort dragging mode.
- Remember mouse and sash position on buttondown event and use them as
  absolute reference during dragging. Avoid delta values from one mousemove to
  the next as this may introduce a skew during dragging and especially when
  coordinate clipping occurs.
- Clear the requested sash position when dragging in live mode.
- Draw the tracker at correct coordinates - taking into account the width of
  the pen used to draw the tracker.
- The old code did not clearly distinguish between live vs. tracking mode in
  some places.

Closes #11076.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@61615 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2009-08-05 17:25:27 +00:00
parent e2495f725c
commit b5a9b87e16
2 changed files with 89 additions and 105 deletions

View File

@ -193,6 +193,9 @@ public:
// Handles mouse events
void OnMouseEvent(wxMouseEvent& ev);
// Aborts dragging mode
void OnMouseCaptureLost(wxMouseCaptureLostEvent& event);
// Adjusts the panes
void OnSize(wxSizeEvent& event);
@ -275,16 +278,16 @@ protected:
wxWindow* m_windowOne;
wxWindow* m_windowTwo;
int m_dragMode;
int m_oldX;
int m_oldY;
int m_oldX; // current tracker position if not live mode
int m_oldY; // current tracker position if not live mode
int m_sashPosition; // Number of pixels from left or top
double m_sashGravity;
int m_sashSize;
wxSize m_lastSize;
int m_requestedSashPosition;
int m_sashPositionCurrent; // while dragging
int m_firstX;
int m_firstY;
wxPoint m_ptStart; // mouse position when dragging started
int m_sashStart; // sash position when dragging started
int m_minimumPaneSize;
wxCursor m_sashCursorWE;
wxCursor m_sashCursorNS;

View File

@ -61,6 +61,7 @@ BEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow)
EVT_PAINT(wxSplitterWindow::OnPaint)
EVT_SIZE(wxSplitterWindow::OnSize)
EVT_MOUSE_EVENTS(wxSplitterWindow::OnMouseEvent)
EVT_MOUSE_CAPTURE_LOST(wxSplitterWindow::OnMouseCaptureLost)
#if defined( __WXMSW__ ) || defined( __WXMAC__)
EVT_SET_CURSOR(wxSplitterWindow::OnSetCursor)
@ -71,6 +72,19 @@ END_EVENT_TABLE()
WX_DELEGATE_TO_CONTROL_CONTAINER(wxSplitterWindow, wxWindow)
static bool IsLive(wxSplitterWindow* wnd)
{
// with wxSP_LIVE_UPDATE style the splitter windows are always resized
// following the mouse movement while it drags the sash, without it we only
// draw the sash at the new position but only resize the windows when the
// dragging is finished
#if defined( __WXMAC__ ) && defined(TARGET_API_MAC_OSX) && TARGET_API_MAC_OSX == 1
return true; // Mac can't paint outside paint event - always need live mode
#else
return wnd->HasFlag(wxSP_LIVE_UPDATE);
#endif
}
bool wxSplitterWindow::Create(wxWindow *parent, wxWindowID id,
const wxPoint& pos,
const wxSize& size,
@ -84,10 +98,6 @@ bool wxSplitterWindow::Create(wxWindow *parent, wxWindowID id,
style &= ~wxBORDER_MASK;
style |= wxBORDER_NONE;
#ifdef __WXMAC__
// CoreGraphics can't paint sash feedback
style |= wxSP_LIVE_UPDATE;
#endif
if ( !wxWindow::Create(parent, id, pos, size, style, name) )
return false;
@ -121,8 +131,7 @@ void wxSplitterWindow::Init()
m_dragMode = wxSPLIT_DRAG_NONE;
m_oldX = 0;
m_oldY = 0;
m_firstX = 0;
m_firstY = 0;
m_sashStart = 0;
m_sashPosition = m_requestedSashPosition = 0;
m_sashGravity = 0.0;
m_sashSize = -1; // -1 means use the native sash size
@ -217,17 +226,8 @@ void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event)
return;
}
// with wxSP_LIVE_UPDATE style the splitter windows are always resized
// following the mouse movement while it drags the sash, without it we only
// draw the sash at the new position but only resize the windows when the
// dragging is finished
#if defined( __WXMAC__ )
// FIXME : this should be usable also with no live update, but then this
// currently is not visible
bool isLive = true;
#else
bool isLive = HasFlag(wxSP_LIVE_UPDATE);
#endif
bool isLive = IsLive(this);
if (event.LeftDown())
{
if ( SashHitTest(x, y) )
@ -245,13 +245,13 @@ void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event)
// shadow sash
m_sashPositionCurrent = m_sashPosition;
DrawSashTracker(x, y);
m_oldX = (m_splitMode == wxSPLIT_VERTICAL ? m_sashPositionCurrent : x);
m_oldY = (m_splitMode != wxSPLIT_VERTICAL ? m_sashPositionCurrent : y);
DrawSashTracker(m_oldX, m_oldY);
}
m_oldX = x;
m_oldY = y;
SetResizeCursor();
m_ptStart = wxPoint(x,y);
m_sashStart = m_sashPosition;
return;
}
}
@ -279,10 +279,9 @@ void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event)
// the position of the click doesn't exactly correspond to
// m_sashPosition, rather it changes it by the distance by which the
// mouse has moved
int diff = m_splitMode == wxSPLIT_VERTICAL ? x - m_oldX : y - m_oldY;
int diff = m_splitMode == wxSPLIT_VERTICAL ? x - m_ptStart.x : y - m_ptStart.y;
int posSashOld = isLive ? m_sashPosition : m_sashPositionCurrent;
int posSashNew = OnSashPositionChanging(posSashOld + diff);
int posSashNew = OnSashPositionChanging(m_sashStart + diff);
if ( posSashNew == -1 )
{
// change not allowed
@ -336,62 +335,53 @@ void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event)
}
else if (event.Dragging() && (m_dragMode == wxSPLIT_DRAG_DRAGGING))
{
int diff = m_splitMode == wxSPLIT_VERTICAL ? x - m_oldX : y - m_oldY;
if ( !diff )
{
// nothing to do, mouse didn't really move far enough
return;
}
int diff = m_splitMode == wxSPLIT_VERTICAL ? x - m_ptStart.x : y - m_ptStart.y;
int posSashOld = isLive ? m_sashPosition : m_sashPositionCurrent;
int posSashNew = OnSashPositionChanging(posSashOld + diff);
int posSashNew = OnSashPositionChanging(m_sashStart + diff);
if ( posSashNew == -1 )
{
// change not allowed
return;
}
if ( posSashNew == m_sashPosition )
return;
// Erase old tracker
if ( !isLive )
{
DrawSashTracker(m_oldX, m_oldY);
}
if ( posSashNew == m_sashPositionCurrent )
return;
if (m_splitMode == wxSPLIT_VERTICAL)
x = posSashNew;
else
y = posSashNew;
// Remember old positions
m_oldX = x;
m_oldY = y;
#ifdef __WXMSW__
// As we captured the mouse, we may get the mouse events from outside
// our window - for example, negative values in x, y. This has a weird
// consequence under MSW where we use unsigned values sometimes and
// signed ones other times: the coordinates turn as big positive
// numbers and so the sash is drawn on the *right* side of the window
// instead of the left (or bottom instead of top). Correct this.
if ( (short)m_oldX < 0 )
m_oldX = 0;
if ( (short)m_oldY < 0 )
m_oldY = 0;
#endif // __WXMSW__
// Draw new one
if ( !isLive )
{
m_sashPositionCurrent = posSashNew;
// Erase old tracker
DrawSashTracker(m_oldX, m_oldY);
m_oldX = (m_splitMode == wxSPLIT_VERTICAL ? m_sashPositionCurrent : x);
m_oldY = (m_splitMode != wxSPLIT_VERTICAL ? m_sashPositionCurrent : y);
#ifdef __WXMSW__
// As we captured the mouse, we may get the mouse events from outside
// our window - for example, negative values in x, y. This has a weird
// consequence under MSW where we use unsigned values sometimes and
// signed ones other times: the coordinates turn as big positive
// numbers and so the sash is drawn on the *right* side of the window
// instead of the left (or bottom instead of top). Correct this.
if ( (short)m_oldX < 0 )
m_oldX = 0;
if ( (short)m_oldY < 0 )
m_oldY = 0;
#endif // __WXMSW__
// Draw new one
DrawSashTracker(m_oldX, m_oldY);
}
else
{
if ( posSashNew == m_sashPosition )
return;
DoSetSashPosition(posSashNew);
// in live mode, the new position is the actual sash position, clear requested position!
m_requestedSashPosition = INT_MAX;
m_needUpdating = true;
}
}
@ -405,6 +395,22 @@ void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event)
}
}
void wxSplitterWindow::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
{
if (m_dragMode != wxSPLIT_DRAG_DRAGGING)
return;
m_dragMode = wxSPLIT_DRAG_NONE;
SetCursor(* wxSTANDARD_CURSOR);
// Erase old tracker
if ( !IsLive(this) )
{
DrawSashTracker(m_oldX, m_oldY);
}
}
void wxSplitterWindow::OnSize(wxSizeEvent& event)
{
// only process this message if we're not iconized - otherwise iconizing
@ -535,33 +541,15 @@ void wxSplitterWindow::DrawSashTracker(int x, int y)
if ( m_splitMode == wxSPLIT_VERTICAL )
{
x1 = x; y1 = 2;
x2 = x; y2 = h-2;
if ( x1 > w )
{
x1 = w; x2 = w;
}
else if ( x1 < 0 )
{
x1 = 0; x2 = 0;
}
x1 = x2 = wxClip(x, 0, w) + m_sashTrackerPen->GetWidth()/2;
y1 = 2;
y2 = h-2;
}
else
{
x1 = 2; y1 = y;
x2 = w-2; y2 = y;
if ( y1 > h )
{
y1 = h;
y2 = h;
}
else if ( y1 < 0 )
{
y1 = 0;
y2 = 0;
}
y1 = y2 = wxClip(y, 0, h) + m_sashTrackerPen->GetWidth()/2;
x1 = 2;
x2 = w-2;
}
ClientToScreen(&x1, &y1);
@ -767,15 +755,8 @@ bool wxSplitterWindow::DoSplit(wxSplitMode mode,
m_windowOne = window1;
m_windowTwo = window2;
// remember the sash position we want to set for later if we can't set it
// right now (e.g. because the window is too small)
m_requestedSashPosition = sashPosition;
m_checkRequestedSashPosition = false;
DoSetSashPosition(ConvertSashPosition(sashPosition));
SizeWindows();
SetSashPosition(sashPosition, true);
return true;
}
@ -983,12 +964,12 @@ int wxSplitterWindow::OnSashPositionChanging(int newSashPosition)
{
// If resultant pane would be too small, enlarge it
newSashPosition = AdjustSashPosition(newSashPosition);
}
// If the result is out of bounds it means minimum size is too big,
// so split window in half as best compromise.
if ( newSashPosition < 0 || newSashPosition > window_size )
newSashPosition = window_size / 2;
// If the result is out of bounds it means minimum size is too big,
// so split window in half as best compromise.
if ( newSashPosition < 0 || newSashPosition > window_size )
newSashPosition = window_size / 2;
}
// now let the event handler have it
//