NS_IMETHODIMP nsListBoxBodyFrame::ScrollToIndex(PRInt32 aRowIndex) { if (( aRowIndex < 0 ) || (mRowHeight == 0)) return NS_OK; PRInt32 newIndex = aRowIndex; PRInt32 delta = mCurrentIndex > newIndex ? mCurrentIndex - newIndex : newIndex - mCurrentIndex; PRBool up = newIndex < mCurrentIndex; // Check to be sure we're not scrolling off the bottom of the tree PRInt32 lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight); if (lastPageTopRow < 0) lastPageTopRow = 0; if (aRowIndex > lastPageTopRow) return NS_OK; mCurrentIndex = newIndex; // Since we're going to flush anyway, we need to not do this off an event DoInternalPositionChangedSync(up, delta); // This change has to happen immediately. // Flush any pending reflow commands. // XXXbz why, exactly? mContent->GetDocument()->FlushPendingNotifications(Flush_Layout); return NS_OK; }
NS_IMETHODIMP nsListBoxBodyFrame::VisibilityChanged(nsISupports* aScrollbar, PRBool aVisible) { PRInt32 lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight); if (lastPageTopRow < 0) lastPageTopRow = 0; PRInt32 delta = mCurrentIndex - lastPageTopRow; if (delta > 0) { mCurrentIndex = lastPageTopRow; InternalPositionChanged(PR_TRUE, delta); } return NS_OK; }
NS_IMETHODIMP nsListBoxBodyFrame::VisibilityChanged(bool aVisible) { if (mRowHeight == 0) return NS_OK; PRInt32 lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight); if (lastPageTopRow < 0) lastPageTopRow = 0; PRInt32 delta = mCurrentIndex - lastPageTopRow; if (delta > 0) { mCurrentIndex = lastPageTopRow; InternalPositionChanged(true, delta); } return NS_OK; }
void nsListBoxBodyFrame::CreateRows() { // Get our client rect. nsRect clientRect; GetClientRect(clientRect); // Get the starting y position and the remaining available // height. nscoord availableHeight = GetAvailableHeight(); if (availableHeight <= 0) { bool fixed = (GetFixedRowSize() != -1); if (fixed) availableHeight = 10; else return; } // get the first tree box. If there isn't one create one. bool created = false; nsIBox* box = GetFirstItemBox(0, &created); nscoord rowHeight = GetRowHeightAppUnits(); while (box) { if (created && mRowsToPrepend > 0) --mRowsToPrepend; // if the row height is 0 then fail. Wait until someone // laid out and sets the row height. if (rowHeight == 0) return; availableHeight -= rowHeight; // should we continue? Is the enought height? if (!ContinueReflow(availableHeight)) break; // get the next tree box. Create one if needed. box = GetNextItemBox(box, 0, &created); } mRowsToPrepend = 0; mLinkupFrame = nsnull; }
nsresult nsListBoxBodyFrame::EnsureIndexIsVisible(PRInt32 aRowIndex) { if (aRowIndex < 0) return NS_ERROR_ILLEGAL_VALUE; PRInt32 rows = 0; if (mRowHeight) rows = GetAvailableHeight()/mRowHeight; if (rows <= 0) rows = 1; PRInt32 bottomIndex = mCurrentIndex + rows; // if row is visible, ignore if (mCurrentIndex <= aRowIndex && aRowIndex < bottomIndex) return NS_OK; PRInt32 delta; bool up = aRowIndex < mCurrentIndex; if (up) { delta = mCurrentIndex - aRowIndex; mCurrentIndex = aRowIndex; } else { // Check to be sure we're not scrolling off the bottom of the tree if (aRowIndex >= GetRowCount()) return NS_ERROR_ILLEGAL_VALUE; // Bring it just into view. delta = 1 + (aRowIndex-bottomIndex); mCurrentIndex += delta; } // Safe to not go off an event here, since this is coming from the // box object. DoInternalPositionChangedSync(up, delta); return NS_OK; }
nsresult nsListBoxBodyFrame::DoInternalPositionChanged(bool aUp, PRInt32 aDelta) { if (aDelta == 0) return NS_OK; nsRefPtr<nsPresContext> presContext(PresContext()); nsBoxLayoutState state(presContext); // begin timing how long it takes to scroll a row PRTime start = PR_Now(); nsWeakFrame weakThis(this); mContent->GetDocument()->FlushPendingNotifications(Flush_Layout); if (!weakThis.IsAlive()) { return NS_OK; } { nsAutoScriptBlocker scriptBlocker; PRInt32 visibleRows = 0; if (mRowHeight) visibleRows = GetAvailableHeight()/mRowHeight; if (aDelta < visibleRows) { PRInt32 loseRows = aDelta; if (aUp) { // scrolling up, destroy rows from the bottom downwards ReverseDestroyRows(loseRows); mRowsToPrepend += aDelta; mLinkupFrame = nsnull; } else { // scrolling down, destroy rows from the top upwards DestroyRows(loseRows); mRowsToPrepend = 0; } } else { // We have scrolled so much that all of our current frames will // go off screen, so blow them all away. Weeee! nsIFrame *currBox = mFrames.FirstChild(); nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor(); fc->BeginUpdate(); while (currBox) { nsIFrame *nextBox = currBox->GetNextSibling(); RemoveChildFrame(state, currBox); currBox = nextBox; } fc->EndUpdate(); } // clear frame markers so that CreateRows will re-create mTopFrame = mBottomFrame = nsnull; mYPosition = mCurrentIndex*mRowHeight; mScrolling = true; presContext->PresShell()-> FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN); } if (!weakThis.IsAlive()) { return NS_OK; } // Flush calls CreateRows // XXXbz there has to be a better way to do this than flushing! presContext->PresShell()->FlushPendingNotifications(Flush_Layout); if (!weakThis.IsAlive()) { return NS_OK; } mScrolling = false; VerticalScroll(mYPosition); PRTime end = PR_Now(); PRTime difTime; LL_SUB(difTime, end, start); PRInt32 newTime; LL_L2I(newTime, difTime); newTime /= aDelta; // average old and new mTimePerRow = (newTime + mTimePerRow)/2; return NS_OK; }
nsresult nsListBoxBodyFrame::GetNumberOfVisibleRows(PRInt32 *aResult) { *aResult= mRowHeight ? GetAvailableHeight() / mRowHeight : 0; return NS_OK; }