X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b544a27802734a8d45a8e59c9563e1d9eb64c10c..1ab440bc203fc700744039abdcff12bdd6ac6f0a:/src/generic/vscroll.cpp diff --git a/src/generic/vscroll.cpp b/src/generic/vscroll.cpp index 0d71a2fc67..59a65f856e 100644 --- a/src/generic/vscroll.cpp +++ b/src/generic/vscroll.cpp @@ -17,6 +17,17 @@ // headers // ---------------------------------------------------------------------------- +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/sizer.h" +#endif + #include "wx/vscroll.h" // ---------------------------------------------------------------------------- @@ -26,6 +37,9 @@ BEGIN_EVENT_TABLE(wxVScrolledWindow, wxPanel) EVT_SIZE(wxVScrolledWindow::OnSize) EVT_SCROLLWIN(wxVScrolledWindow::OnScroll) +#if wxUSE_MOUSEWHEEL + EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel) +#endif END_EVENT_TABLE() @@ -33,6 +47,8 @@ END_EVENT_TABLE() // implementation // ============================================================================ +IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel) + // ---------------------------------------------------------------------------- // initialization // ---------------------------------------------------------------------------- @@ -47,12 +63,48 @@ void wxVScrolledWindow::Init() m_nVisible = 1; m_heightTotal = 0; + +#if wxUSE_MOUSEWHEEL + m_sumWheelRotation = 0; +#endif } // ---------------------------------------------------------------------------- // various helpers // ---------------------------------------------------------------------------- +wxCoord wxVScrolledWindow::EstimateTotalHeight() const +{ + // estimate the total height: it is impossible to call + // OnGetLineHeight() for every line because there may be too many of + // them, so we just make a guess using some lines in the beginning, + // some in the end and some in the middle + static const size_t NUM_LINES_TO_SAMPLE = 10; + + wxCoord heightTotal; + if ( m_lineMax < 3*NUM_LINES_TO_SAMPLE ) + { + // in this case calculating exactly is faster and more correct than + // guessing + heightTotal = GetLinesHeight(0, m_lineMax); + } + else // too many lines to calculate exactly + { + // look at some lines in the beginning/middle/end + heightTotal = + GetLinesHeight(0, NUM_LINES_TO_SAMPLE) + + GetLinesHeight(m_lineMax - NUM_LINES_TO_SAMPLE, m_lineMax) + + GetLinesHeight(m_lineMax/2 - NUM_LINES_TO_SAMPLE/2, + m_lineMax/2 + NUM_LINES_TO_SAMPLE/2); + + // use the height of the lines we looked as the average + heightTotal = (wxCoord) + (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax); + } + + return heightTotal; +} + wxCoord wxVScrolledWindow::GetLinesHeight(size_t lineMin, size_t lineMax) const { if ( lineMin == lineMax ) @@ -74,7 +126,7 @@ wxCoord wxVScrolledWindow::GetLinesHeight(size_t lineMin, size_t lineMax) const return height; } -size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast) +size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast, bool full) { const wxCoord hWindow = GetClientSize().y; @@ -88,7 +140,13 @@ size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast) if ( h > hWindow ) { - lineFirst++; + // for this line to be fully visible we need to go one line + // down, but if it is enough for it to be only partly visible then + // this line will do as well + if ( full ) + { + lineFirst++; + } break; } @@ -102,6 +160,13 @@ size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast) return lineFirst; } +void wxVScrolledWindow::RemoveScrollbar() +{ + m_lineFirst = 0; + m_nVisible = m_lineMax; + SetScrollbar(wxVERTICAL, 0, 0, 0); +} + void wxVScrolledWindow::UpdateScrollbar() { // see how many lines can we fit on screen @@ -117,6 +182,25 @@ void wxVScrolledWindow::UpdateScrollbar() h += OnGetLineHeight(line); } + // if we still have remaining space below, maybe we can fit everything? + if ( h < hWindow ) + { + wxCoord hAll = h; + for ( size_t lineFirst = m_lineFirst; lineFirst > 0; lineFirst-- ) + { + hAll += OnGetLineHeight(m_lineFirst - 1); + if ( hAll > hWindow ) + break; + } + + if ( hAll < hWindow ) + { + // we don't need scrollbar at all + RemoveScrollbar(); + return; + } + } + m_nVisible = line - m_lineFirst; int pageSize = m_nVisible; @@ -141,37 +225,19 @@ void wxVScrolledWindow::SetLineCount(size_t count) // save the number of lines m_lineMax = count; + // and our estimate for their total height + m_heightTotal = EstimateTotalHeight(); - // estimate the total height: it is impossible to call - // OnGetLineHeight() for every line because there may be too many of - // them, so we just make a guess using some lines in the beginning, - // some in the end and some in the middle - static const size_t NUM_LINES_TO_SAMPLE = 10; - - if ( count < 3*NUM_LINES_TO_SAMPLE ) + // recalculate the scrollbars parameters + if ( count ) { - // in this case calculating exactly is faster and more correct than - // guessing - m_heightTotal = GetLinesHeight(0, m_lineMax); + m_lineFirst = 1; // make sure it is != 0 + ScrollToLine(0); } - else // too many lines to calculate exactly + else // no items { - // look at some lines in the beginning/middle/end - m_heightTotal = - GetLinesHeight(0, NUM_LINES_TO_SAMPLE) + - GetLinesHeight(count - NUM_LINES_TO_SAMPLE, count) + - GetLinesHeight(count/2 - NUM_LINES_TO_SAMPLE/2, - count/2 + NUM_LINES_TO_SAMPLE/2); - - // use the height of the lines we looked as the average - m_heightTotal = (wxCoord) - (((float)m_heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax); + RemoveScrollbar(); } - - - // recalculate the scrollbars parameters - m_lineFirst = 1; // make sure it is != 0 - ScrollToLine(0); } void wxVScrolledWindow::RefreshLine(size_t line) @@ -187,7 +253,7 @@ void wxVScrolledWindow::RefreshLine(size_t line) wxRect rect; rect.width = GetClientSize().x; rect.height = OnGetLineHeight(line); - for ( size_t n = GetFirstVisibleLine(); n < line; n++ ) + for ( size_t n = GetVisibleBegin(); n < line; n++ ) { rect.y += OnGetLineHeight(n); } @@ -196,10 +262,69 @@ void wxVScrolledWindow::RefreshLine(size_t line) RefreshRect(rect); } +void wxVScrolledWindow::RefreshLines(size_t from, size_t to) +{ + wxASSERT_MSG( from <= to, _T("RefreshLines(): empty range") ); + + // clump the range to just the visible lines -- it is useless to refresh + // the other ones + if ( from < GetVisibleBegin() ) + from = GetVisibleBegin(); + + if ( to >= GetVisibleEnd() ) + to = GetVisibleEnd(); + else + to++; + + // calculate the rect occupied by these lines on screen + wxRect rect; + rect.width = GetClientSize().x; + for ( size_t nBefore = GetVisibleBegin(); nBefore < from; nBefore++ ) + { + rect.y += OnGetLineHeight(nBefore); + } + + for ( size_t nBetween = from; nBetween < to; nBetween++ ) + { + rect.height += OnGetLineHeight(nBetween); + } + + // do refresh it + RefreshRect(rect); +} + +void wxVScrolledWindow::RefreshAll() +{ + UpdateScrollbar(); + + Refresh(); +} + +bool wxVScrolledWindow::Layout() +{ + if ( GetSizer() ) + { + // adjust the sizer dimensions/position taking into account the + // virtual size and scrolled position of the window. + + int w, h; + GetVirtualSize(&w, &h); + + // x is always 0 so no variable needed + int y = -GetLinesHeight(0, GetFirstVisibleLine()); + + GetSizer()->SetDimension(0, y, w, h); + return true; + } + + // fall back to default for LayoutConstraints + return wxPanel::Layout(); +} + int wxVScrolledWindow::HitTest(wxCoord WXUNUSED(x), wxCoord y) const { - const size_t lineMax = GetLastVisibleLine(); - for ( size_t line = GetFirstVisibleLine(); line <= lineMax; line++ ) + const size_t lineMax = GetVisibleEnd(); + for ( size_t line = GetVisibleBegin(); line < lineMax; line++ ) { y -= OnGetLineHeight(line); if ( y < 0 ) @@ -223,7 +348,7 @@ bool wxVScrolledWindow::ScrollToLine(size_t line) // determine the real first line to scroll to: we shouldn't scroll beyond // the end - size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1); + size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true); if ( line > lineFirstLast ) line = lineFirstLast; @@ -236,8 +361,8 @@ bool wxVScrolledWindow::ScrollToLine(size_t line) // remember the currently shown lines for the refresh code below - size_t lineFirstOld = GetFirstVisibleLine(), - lineLastOld = GetLastVisibleLine(); + size_t lineFirstOld = GetVisibleBegin(), + lineLastOld = GetVisibleEnd(); m_lineFirst = line; @@ -248,8 +373,8 @@ bool wxVScrolledWindow::ScrollToLine(size_t line) // finally refresh the display -- but only redraw as few lines as possible // to avoid flicker - if ( GetFirstVisibleLine() > lineLastOld || - GetLastVisibleLine() < lineFirstOld ) + if ( GetVisibleBegin() >= lineLastOld || + GetVisibleEnd() <= lineFirstOld ) { // the simplest case: we don't have any old lines left, just redraw // everything @@ -257,7 +382,7 @@ bool wxVScrolledWindow::ScrollToLine(size_t line) } else // overlap between the lines we showed before and should show now { - ScrollWindow(0, GetLinesHeight(GetFirstVisibleLine(), lineFirstOld)); + ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld)); } return true; @@ -281,12 +406,14 @@ bool wxVScrolledWindow::ScrollPages(int pages) int line; if ( pages > 0 ) { - line = GetLastVisibleLine(); + line = GetVisibleEnd(); + if ( line ) + line--; pages--; } else // pages < 0 { - line = FindFirstFromBottom(GetFirstVisibleLine()); + line = FindFirstFromBottom(GetVisibleBegin()); pages++; } @@ -312,6 +439,7 @@ void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event) size_t lineFirstNew; const wxEventType evtType = event.GetEventType(); + if ( evtType == wxEVT_SCROLLWIN_TOP ) { lineFirstNew = 0; @@ -334,23 +462,23 @@ void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event) } else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN ) { - lineFirstNew = GetLastVisibleLine(); + lineFirstNew = GetVisibleEnd(); + if ( lineFirstNew ) + lineFirstNew--; } - else // unknown scroll event? + else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE ) { - if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE ) - { - lineFirstNew = event.GetPosition(); - } - else - { - wxASSERT_MSG( evtType == wxEVT_SCROLLWIN_THUMBTRACK, - _T("unknown scroll event type?") ); + lineFirstNew = event.GetPosition(); + } + else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK ) + { + lineFirstNew = event.GetPosition(); + } - // don't do anything, otherwise dragging the thumb around would - // be too slow - return; - } + else // unknown scroll event? + { + wxFAIL_MSG( _T("unknown scroll event type?") ); + return; } ScrollToLine(lineFirstNew); @@ -360,3 +488,26 @@ void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event) #endif // __WXMAC__ } +#if wxUSE_MOUSEWHEEL + +void wxVScrolledWindow::OnMouseWheel(wxMouseEvent& event) +{ + m_sumWheelRotation += event.GetWheelRotation(); + int delta = event.GetWheelDelta(); + + // how much to scroll this time + int units_to_scroll = -(m_sumWheelRotation/delta); + if ( !units_to_scroll ) + return; + + m_sumWheelRotation += units_to_scroll*delta; + + if ( !event.IsPageScroll() ) + ScrollLines( units_to_scroll*event.GetLinesPerAction() ); + else + // scroll pages instead of lines + ScrollPages( units_to_scroll ); +} + +#endif // wxUSE_MOUSEWHEEL +