From: Vadim Zeitlin Date: Wed, 5 Aug 2009 17:25:27 +0000 (+0000) Subject: wxSplitterWindow mouse capture improvements and cleanup. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/b5a9b87e1620e52f1f13d2619b4677f2b365c93f?ds=inline 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 --- diff --git a/include/wx/generic/splitter.h b/include/wx/generic/splitter.h index d009107cd0..7244535737 100644 --- a/include/wx/generic/splitter.h +++ b/include/wx/generic/splitter.h @@ -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; diff --git a/src/generic/splitter.cpp b/src/generic/splitter.cpp index 3b967a86b0..e358c875de 100644 --- a/src/generic/splitter.cpp +++ b/src/generic/splitter.cpp @@ -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; + m_sashPositionCurrent = posSashNew; - // Remember old positions - m_oldX = x; - m_oldY = y; + // 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; + // 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; - + // 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 //