diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 516cf9a25110..735ef97b901a 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -8262,8 +8262,10 @@ nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, if (aContainer) { nsCOMPtr tag; aContainer->GetTag(*getter_AddRefs(tag)); - if (tag && (tag.get() == nsXULAtoms::treechildren || - tag.get() == nsXULAtoms::treeitem)) { + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; + PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; + + if (treeChildren || treeItem) { // Walk up to the outermost tree row group frame and tell it that // content was added. nsCOMPtr parent; @@ -8287,15 +8289,40 @@ nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, // Get the primary frame for the parent of the child that's being added. nsIFrame* innerFrame = GetFrameFor(shell, aPresContext, aContainer); - - treeRowGroup->ClearRowGroupInfo(); - + + nsBoxLayoutState state(aPresContext); + nsXULTreeGroupFrame* innerGroup = (nsXULTreeGroupFrame*) innerFrame; if (innerGroup) { - nsBoxLayoutState state(aPresContext); innerGroup->MarkDirtyChildren(state); } - shell->FlushPendingNotifications(); + else { + treeRowGroup->MarkDirtyChildren(state); + + // Resolve our style context to find out if we need to clear out our + // undisplayed content. + if (treeChildren) { + nsCOMPtr parent; + aContainer->GetParent(*getter_AddRefs(parent)); + if (parent) { + nsAutoString open; + parent->GetAttribute(kNameSpaceID_None, nsXULAtoms::open, open); + if (open.EqualsIgnoreCase("true")) { + // Clear our undisplayed content. + nsCOMPtr shell; + aPresContext->GetShell(getter_AddRefs(shell)); + nsCOMPtr frameManager; + shell->GetFrameManager(getter_AddRefs(frameManager)); + frameManager->ClearUndisplayedContentIn(aContainer, parent); + } + } + } + } + + treeRowGroup->RegenerateRowGroupInfo(0); + + if (!treeRowGroup->IsBatching()) + shell->FlushPendingNotifications(); return NS_OK; } @@ -8567,8 +8594,9 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, if (aContainer) { nsCOMPtr tag; aContainer->GetTag(*getter_AddRefs(tag)); - if (tag && (tag.get() == nsXULAtoms::treechildren || - tag.get() == nsXULAtoms::treeitem)) { + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; + PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; + if (treeChildren || treeItem) { // Walk up to the outermost tree row group frame and tell it that // content was added. nsCOMPtr parent; @@ -8600,7 +8628,7 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, nsIFrame* prevSibling = FindPreviousSibling(shell, aContainer, aIndexInContainer); - if (prevSibling || innerFrame) { + if (innerFrame) { // We're onscreen, but because of the fact that we can be called to // "kill" a displayed frame (e.g., when you close a tree node), we // have to see if this slaying is taking place. If so, then we don't @@ -8617,16 +8645,18 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, const nsStyleDisplay* display = (const nsStyleDisplay*) styleContext->GetStyleData(eStyleStruct_Display); - if (NS_STYLE_DISPLAY_NONE == display->mDisplay) { - - nsFrameConstructorState state(aPresContext, mFixedContainingBlock, + nsFrameConstructorState state(aPresContext, mFixedContainingBlock, GetAbsoluteContainingBlock(aPresContext, innerFrame), GetFloaterContainingBlock(aPresContext, innerFrame), aFrameState); + + if (NS_STYLE_DISPLAY_NONE == display->mDisplay && treeItem) { state.mFrameManager->SetUndisplayedContent(aChild, styleContext); return NS_OK; } - + } + + if (prevSibling || innerFrame) { nsXULTreeGroupFrame* innerGroup = (nsXULTreeGroupFrame*) innerFrame; nsBoxLayoutState state(aPresContext); @@ -8638,11 +8668,12 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, else innerGroup->MarkDirtyChildren(state); } - treeRowGroup->ClearRowGroupInfo(); + treeRowGroup->RegenerateRowGroupInfo(0); treeRowGroup->MarkDirtyChildren(state); } - shell->FlushPendingNotifications(); + if (!treeRowGroup->IsBatching()) + shell->FlushPendingNotifications(); return NS_OK; } } @@ -9236,15 +9267,18 @@ nsCSSFrameConstructor::ContentRemoved(nsIPresContext* aPresContext, if (aContainer) { nsCOMPtr tag; aContainer->GetTag(*getter_AddRefs(tag)); - if (tag.get() == nsXULAtoms::treechildren || - tag.get() == nsXULAtoms::treeitem) { + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; + PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; + + if (treeChildren || treeItem) { + PRInt32 onScreenDelta = 0; if (childFrame) { // Convert to a tree row group frame. nsIFrame* parentFrame; childFrame->GetParent(&parentFrame); nsXULTreeGroupFrame* treeRowGroup = (nsXULTreeGroupFrame*)parentFrame; if (treeRowGroup) { - treeRowGroup->OnContentRemoved(aPresContext, childFrame, aIndexInContainer); + treeRowGroup->OnContentRemoved(aPresContext, childFrame, aIndexInContainer, onScreenDelta); } } { @@ -9270,10 +9304,16 @@ nsCSSFrameConstructor::ContentRemoved(nsIPresContext* aPresContext, // Convert to a tree row group frame. nsXULTreeOuterGroupFrame* treeRowGroup = (nsXULTreeOuterGroupFrame*)parentFrame; if (treeRowGroup) { + // If a tree item is removed, try to find an item we can use + // to detect if the removed item was above our current scroll + // position. + treeRowGroup->RegenerateRowGroupInfo(onScreenDelta); + nsBoxLayoutState state(aPresContext); treeRowGroup->MarkDirtyChildren(state); - treeRowGroup->ClearRowGroupInfo(); - shell->FlushPendingNotifications(); + + if (!treeRowGroup->IsBatching()) + shell->FlushPendingNotifications(); } return NS_OK; } diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index a45986d082e0..e8fe515eb1c4 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -3127,9 +3127,7 @@ PresShell::AppendReflowCommand(nsIReflowCommand* aReflowCommand) (!gAsyncReflowDuringDocLoad && !mBatchReflows && !mDocumentLoading)) { // If we're in the middle of a drag, process it right away (needed for mac, // might as well do it on all platforms just to keep the code paths the same). - if ( IsDragInProgress() ) - FlushPendingNotifications(); - else + if ( !IsDragInProgress() ) PostReflowEvent(); } diff --git a/layout/html/base/src/nsPresShell.cpp b/layout/html/base/src/nsPresShell.cpp index a45986d082e0..e8fe515eb1c4 100644 --- a/layout/html/base/src/nsPresShell.cpp +++ b/layout/html/base/src/nsPresShell.cpp @@ -3127,9 +3127,7 @@ PresShell::AppendReflowCommand(nsIReflowCommand* aReflowCommand) (!gAsyncReflowDuringDocLoad && !mBatchReflows && !mDocumentLoading)) { // If we're in the middle of a drag, process it right away (needed for mac, // might as well do it on all platforms just to keep the code paths the same). - if ( IsDragInProgress() ) - FlushPendingNotifications(); - else + if ( !IsDragInProgress() ) PostReflowEvent(); } diff --git a/layout/html/style/src/nsCSSFrameConstructor.cpp b/layout/html/style/src/nsCSSFrameConstructor.cpp index 516cf9a25110..735ef97b901a 100644 --- a/layout/html/style/src/nsCSSFrameConstructor.cpp +++ b/layout/html/style/src/nsCSSFrameConstructor.cpp @@ -8262,8 +8262,10 @@ nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, if (aContainer) { nsCOMPtr tag; aContainer->GetTag(*getter_AddRefs(tag)); - if (tag && (tag.get() == nsXULAtoms::treechildren || - tag.get() == nsXULAtoms::treeitem)) { + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; + PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; + + if (treeChildren || treeItem) { // Walk up to the outermost tree row group frame and tell it that // content was added. nsCOMPtr parent; @@ -8287,15 +8289,40 @@ nsCSSFrameConstructor::ContentAppended(nsIPresContext* aPresContext, // Get the primary frame for the parent of the child that's being added. nsIFrame* innerFrame = GetFrameFor(shell, aPresContext, aContainer); - - treeRowGroup->ClearRowGroupInfo(); - + + nsBoxLayoutState state(aPresContext); + nsXULTreeGroupFrame* innerGroup = (nsXULTreeGroupFrame*) innerFrame; if (innerGroup) { - nsBoxLayoutState state(aPresContext); innerGroup->MarkDirtyChildren(state); } - shell->FlushPendingNotifications(); + else { + treeRowGroup->MarkDirtyChildren(state); + + // Resolve our style context to find out if we need to clear out our + // undisplayed content. + if (treeChildren) { + nsCOMPtr parent; + aContainer->GetParent(*getter_AddRefs(parent)); + if (parent) { + nsAutoString open; + parent->GetAttribute(kNameSpaceID_None, nsXULAtoms::open, open); + if (open.EqualsIgnoreCase("true")) { + // Clear our undisplayed content. + nsCOMPtr shell; + aPresContext->GetShell(getter_AddRefs(shell)); + nsCOMPtr frameManager; + shell->GetFrameManager(getter_AddRefs(frameManager)); + frameManager->ClearUndisplayedContentIn(aContainer, parent); + } + } + } + } + + treeRowGroup->RegenerateRowGroupInfo(0); + + if (!treeRowGroup->IsBatching()) + shell->FlushPendingNotifications(); return NS_OK; } @@ -8567,8 +8594,9 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, if (aContainer) { nsCOMPtr tag; aContainer->GetTag(*getter_AddRefs(tag)); - if (tag && (tag.get() == nsXULAtoms::treechildren || - tag.get() == nsXULAtoms::treeitem)) { + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; + PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; + if (treeChildren || treeItem) { // Walk up to the outermost tree row group frame and tell it that // content was added. nsCOMPtr parent; @@ -8600,7 +8628,7 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, nsIFrame* prevSibling = FindPreviousSibling(shell, aContainer, aIndexInContainer); - if (prevSibling || innerFrame) { + if (innerFrame) { // We're onscreen, but because of the fact that we can be called to // "kill" a displayed frame (e.g., when you close a tree node), we // have to see if this slaying is taking place. If so, then we don't @@ -8617,16 +8645,18 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, const nsStyleDisplay* display = (const nsStyleDisplay*) styleContext->GetStyleData(eStyleStruct_Display); - if (NS_STYLE_DISPLAY_NONE == display->mDisplay) { - - nsFrameConstructorState state(aPresContext, mFixedContainingBlock, + nsFrameConstructorState state(aPresContext, mFixedContainingBlock, GetAbsoluteContainingBlock(aPresContext, innerFrame), GetFloaterContainingBlock(aPresContext, innerFrame), aFrameState); + + if (NS_STYLE_DISPLAY_NONE == display->mDisplay && treeItem) { state.mFrameManager->SetUndisplayedContent(aChild, styleContext); return NS_OK; } - + } + + if (prevSibling || innerFrame) { nsXULTreeGroupFrame* innerGroup = (nsXULTreeGroupFrame*) innerFrame; nsBoxLayoutState state(aPresContext); @@ -8638,11 +8668,12 @@ nsCSSFrameConstructor::ContentInserted(nsIPresContext* aPresContext, else innerGroup->MarkDirtyChildren(state); } - treeRowGroup->ClearRowGroupInfo(); + treeRowGroup->RegenerateRowGroupInfo(0); treeRowGroup->MarkDirtyChildren(state); } - shell->FlushPendingNotifications(); + if (!treeRowGroup->IsBatching()) + shell->FlushPendingNotifications(); return NS_OK; } } @@ -9236,15 +9267,18 @@ nsCSSFrameConstructor::ContentRemoved(nsIPresContext* aPresContext, if (aContainer) { nsCOMPtr tag; aContainer->GetTag(*getter_AddRefs(tag)); - if (tag.get() == nsXULAtoms::treechildren || - tag.get() == nsXULAtoms::treeitem) { + PRBool treeChildren = tag && tag.get() == nsXULAtoms::treechildren; + PRBool treeItem = tag && tag.get() == nsXULAtoms::treeitem; + + if (treeChildren || treeItem) { + PRInt32 onScreenDelta = 0; if (childFrame) { // Convert to a tree row group frame. nsIFrame* parentFrame; childFrame->GetParent(&parentFrame); nsXULTreeGroupFrame* treeRowGroup = (nsXULTreeGroupFrame*)parentFrame; if (treeRowGroup) { - treeRowGroup->OnContentRemoved(aPresContext, childFrame, aIndexInContainer); + treeRowGroup->OnContentRemoved(aPresContext, childFrame, aIndexInContainer, onScreenDelta); } } { @@ -9270,10 +9304,16 @@ nsCSSFrameConstructor::ContentRemoved(nsIPresContext* aPresContext, // Convert to a tree row group frame. nsXULTreeOuterGroupFrame* treeRowGroup = (nsXULTreeOuterGroupFrame*)parentFrame; if (treeRowGroup) { + // If a tree item is removed, try to find an item we can use + // to detect if the removed item was above our current scroll + // position. + treeRowGroup->RegenerateRowGroupInfo(onScreenDelta); + nsBoxLayoutState state(aPresContext); treeRowGroup->MarkDirtyChildren(state); - treeRowGroup->ClearRowGroupInfo(); - shell->FlushPendingNotifications(); + + if (!treeRowGroup->IsBatching()) + shell->FlushPendingNotifications(); } return NS_OK; } diff --git a/layout/xul/base/public/nsITreeBoxObject.idl b/layout/xul/base/public/nsITreeBoxObject.idl index 509ca9c83e43..c74f7e0a42fb 100644 --- a/layout/xul/base/public/nsITreeBoxObject.idl +++ b/layout/xul/base/public/nsITreeBoxObject.idl @@ -38,6 +38,9 @@ interface nsITreeBoxObject : nsISupports long getNumberOfVisibleRows(); long getIndexOfFirstVisibleRow(); long getRowCount(); + + void beginBatch(); + void endBatch(); }; %{C++ diff --git a/layout/xul/base/public/nsITreeFrame.h b/layout/xul/base/public/nsITreeFrame.h index a13288d9cca5..a41f09c21cd5 100644 --- a/layout/xul/base/public/nsITreeFrame.h +++ b/layout/xul/base/public/nsITreeFrame.h @@ -46,6 +46,8 @@ public: NS_IMETHOD GetNumberOfVisibleRows(PRInt32* aResult) = 0; NS_IMETHOD GetIndexOfFirstVisibleRow(PRInt32* aResult) = 0; NS_IMETHOD GetRowCount(PRInt32* aResult) = 0; + NS_IMETHOD BeginBatch()=0; + NS_IMETHOD EndBatch()=0; }; #endif diff --git a/layout/xul/base/src/nsSliderFrame.cpp b/layout/xul/base/src/nsSliderFrame.cpp index a6f4d16516d9..399a525af344 100644 --- a/layout/xul/base/src/nsSliderFrame.cpp +++ b/layout/xul/base/src/nsSliderFrame.cpp @@ -201,10 +201,6 @@ nsSliderFrame::AttributeChanged(nsIPresContext* aPresContext, scrollbarFrame->GetScrollbarMediator(getter_AddRefs(mediator)); if (mediator) { mediator->PositionChanged(GetCurrentPosition(scrollbar), current); - char ch[100]; - sprintf(ch,"%d", current); - scrollbar->SetAttribute(kNameSpaceID_None, nsXULAtoms::curpos, NS_ConvertASCIItoUCS2(ch), PR_FALSE); - return NS_OK; } } diff --git a/layout/xul/base/src/nsTreeBoxObject.cpp b/layout/xul/base/src/nsTreeBoxObject.cpp index 65259fedf882..d6918549bd46 100644 --- a/layout/xul/base/src/nsTreeBoxObject.cpp +++ b/layout/xul/base/src/nsTreeBoxObject.cpp @@ -133,7 +133,7 @@ NS_IMETHODIMP nsTreeBoxObject::GetItemAtIndex(PRInt32 index, nsIDOMElement **_re /* long getIndexOfItem (in nsIDOMElement item); */ NS_IMETHODIMP nsTreeBoxObject::GetIndexOfItem(nsIDOMElement* aElement, PRInt32 *aResult) { - *aResult = -1; + *aResult = 0; nsIFrame* frame = GetFrame(); if (!frame) @@ -177,6 +177,26 @@ NS_IMETHODIMP nsTreeBoxObject::GetRowCount(PRInt32 *aResult) return treeFrame->GetRowCount(aResult); } +NS_IMETHODIMP nsTreeBoxObject::BeginBatch() +{ + nsIFrame* frame = GetFrame(); + if (!frame) + return NS_OK; + + nsCOMPtr treeFrame(do_QueryInterface(frame)); + return treeFrame->BeginBatch(); +} + +NS_IMETHODIMP nsTreeBoxObject::EndBatch() +{ + nsIFrame* frame = GetFrame(); + if (!frame) + return NS_OK; + + nsCOMPtr treeFrame(do_QueryInterface(frame)); + return treeFrame->EndBatch(); +} + // Creation Routine /////////////////////////////////////////////////////////////////////// nsresult diff --git a/layout/xul/base/src/nsTreeLayout.cpp b/layout/xul/base/src/nsTreeLayout.cpp index 4c4dd05da9ff..a61e4438c9e5 100644 --- a/layout/xul/base/src/nsTreeLayout.cpp +++ b/layout/xul/base/src/nsTreeLayout.cpp @@ -260,7 +260,7 @@ nsTreeLayout::LayoutInternal(nsIBox* aBox, nsBoxLayoutState& aState) box->GetNextBox(&box); } - if (group && (group == outer) && availableHeight > 0) { + if (group && (group == outer)) { // We have enough available height left to add some more rows // Since we can't do this during layout, we post a callback // that will be processed after the reflow completes. @@ -354,6 +354,8 @@ nsTreeLayout::Layout(nsIBox* aBox, nsBoxLayoutState& aState) if (isOuterGroup) { nsXULTreeOuterGroupFrame* outer = (nsXULTreeOuterGroupFrame*) frame; + if (outer->IsBatching()) + return NS_OK; // Always ensure an accurate scrollview position // This is an edge case that was caused by the row height diff --git a/layout/xul/base/src/nsXULTreeFrame.cpp b/layout/xul/base/src/nsXULTreeFrame.cpp index a499ce209ea6..3722a9fb86bb 100644 --- a/layout/xul/base/src/nsXULTreeFrame.cpp +++ b/layout/xul/base/src/nsXULTreeFrame.cpp @@ -245,7 +245,6 @@ nsXULTreeFrame::GetIndexOfItem(nsIPresContext* aPresContext, nsIDOMElement* aEle if (!treeOuterGroup) return NS_OK; // No tree body. Just bail. - *aResult = 0; nsCOMPtr content(do_QueryInterface(aElement)); nsCOMPtr root; treeOuterGroup->GetContent(getter_AddRefs(root)); @@ -290,6 +289,32 @@ nsXULTreeFrame::GetRowCount(PRInt32 *aResult) return XULTreeOuterGroup->GetRowCount(aResult); } +NS_IMETHODIMP +nsXULTreeFrame::BeginBatch() +{ + // Get our treechildren child frame. + nsXULTreeOuterGroupFrame* treeOuterGroup = nsnull; + GetTreeBody(&treeOuterGroup); + + if (!treeOuterGroup) + return NS_OK; // No tree body. Just bail. + + return treeOuterGroup->BeginBatch(); +} + +NS_IMETHODIMP +nsXULTreeFrame::EndBatch() +{ + // Get our treechildren child frame. + nsXULTreeOuterGroupFrame* treeOuterGroup = nsnull; + GetTreeBody(&treeOuterGroup); + + if (!treeOuterGroup) + return NS_OK; // No tree body. Just bail. + + return treeOuterGroup->EndBatch(); +} + void nsXULTreeFrame::GetTreeBody(nsXULTreeOuterGroupFrame** aResult) { diff --git a/layout/xul/base/src/nsXULTreeFrame.h b/layout/xul/base/src/nsXULTreeFrame.h index b68505c6108b..792fecc29312 100644 --- a/layout/xul/base/src/nsXULTreeFrame.h +++ b/layout/xul/base/src/nsXULTreeFrame.h @@ -51,6 +51,8 @@ public: NS_IMETHOD GetNumberOfVisibleRows(PRInt32 *aResult); NS_IMETHOD GetIndexOfFirstVisibleRow(PRInt32 *aResult); NS_IMETHOD GetRowCount(PRInt32* aResult); + NS_IMETHOD BeginBatch(); + NS_IMETHOD EndBatch(); protected: nsXULTreeFrame(nsIPresShell* aPresShell, PRBool aIsRoot = nsnull, nsIBoxLayout* aLayoutManager = nsnull, PRBool aDefaultHorizontal = PR_TRUE); diff --git a/layout/xul/base/src/nsXULTreeGroupFrame.cpp b/layout/xul/base/src/nsXULTreeGroupFrame.cpp index 5649641a121a..4e4f69062b55 100644 --- a/layout/xul/base/src/nsXULTreeGroupFrame.cpp +++ b/layout/xul/base/src/nsXULTreeGroupFrame.cpp @@ -418,17 +418,26 @@ nsXULTreeGroupFrame::OnContentInserted(nsIPresContext* aPresContext, nsIFrame* a // content was inserted if (mTopFrame == nsnull) return; - // if we're inserting content at the top of visible content, - // then ignore it because it would go off-screen - // except of course in the case of the first row, where we're - // actually adding visible content + // if we're inserting the item before the first visible content, + // then ignore it because it will end up off-screen + // (except of course in the case of the first row, where we're + // actually adding visible content) if(aNextSibling == mTopFrame) { - if (aIndex == 0) - // it's the first row, blow away mTopFrame so it can be - // crecreated later + if (aIndex > 0) // We aren't at the front, so we have to be offscreen. + return; // Just bail. + + nsCOMPtr content; + aNextSibling->GetContent(getter_AddRefs(content)); + PRInt32 siblingIndex; + mContent->IndexOf(content, siblingIndex); + + if (siblingIndex == 1 && mOuterFrame->GetYPosition() == 0) + // We just inserted an item in front of the first of our children + // and we're at the top, such that we have to show the row. + // This item is our new visible top row. mTopFrame = nsnull; else - // it's not visible, nothing to do + // The newly inserted row is offscreen. We can just bail. return; } @@ -450,7 +459,7 @@ nsXULTreeGroupFrame::OnContentInserted(nsIPresContext* aPresContext, nsIFrame* a void nsXULTreeGroupFrame::OnContentRemoved(nsIPresContext* aPresContext, nsIFrame* aChildFrame, - PRInt32 aIndex) + PRInt32 aIndex, PRInt32& aOnScreenRowCount) { // if we're removing the top row, the new top row is the next row if (mTopFrame && mTopFrame == aChildFrame) @@ -459,34 +468,14 @@ void nsXULTreeGroupFrame::OnContentRemoved(nsIPresContext* aPresContext, // Go ahead and delete the frame. nsBoxLayoutState state(aPresContext); if (aChildFrame) { + nsCOMPtr slice(do_QueryInterface(aChildFrame)); + if (slice) + slice->GetOnScreenRowCount(&aOnScreenRowCount); + mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, aChildFrame, nsnull); Remove(state, aChildFrame); mFrames.DestroyFrame(aPresContext, aChildFrame); - MarkDirtyChildren(state); - - // Get our old row count. - PRInt32 rowCount = mOuterFrame->GetRowCount(); - - // See if the last row is visible. If it is, we need to pull back - // by the amount of rows that we lose. - PRInt32 index; - mOuterFrame->GetIndexOfFirstVisibleRow(&index); - PRInt32 vis; - mOuterFrame->GetNumberOfVisibleRows(&vis); - - if (index > 0 && index + vis >= rowCount) { - // Danger, Will Robinson, danger! We need to scroll backwards. - mOuterFrame->ClearRowGroupInfo(); - PRInt32 newCount = mOuterFrame->GetRowCount(); - PRInt32 delta = rowCount - newCount; - mOuterFrame->ScrollToIndex(index-delta); - if (index-delta <= 0) { - // Repaint the world. - mOuterFrame->Redraw(state, nsnull, PR_FALSE); - } - return; - } } MarkDirtyChildren(state); diff --git a/layout/xul/base/src/nsXULTreeGroupFrame.h b/layout/xul/base/src/nsXULTreeGroupFrame.h index 24d8256d83b2..d52f4a778fd7 100644 --- a/layout/xul/base/src/nsXULTreeGroupFrame.h +++ b/layout/xul/base/src/nsXULTreeGroupFrame.h @@ -86,7 +86,7 @@ public: // Responses to changes void OnContentInserted(nsIPresContext* aPresContext, nsIFrame* aNextSibling, PRInt32 aIndex); - void OnContentRemoved(nsIPresContext* aPresContext, nsIFrame* aChildFrame, PRInt32 aIndex); + void OnContentRemoved(nsIPresContext* aPresContext, nsIFrame* aChildFrame, PRInt32 aIndex, PRInt32& aOnScreenRowCount); // nsIXULTreeSlice NS_IMETHOD IsOutermostFrame(PRBool* aResult) { *aResult = PR_FALSE; return NS_OK; }; diff --git a/layout/xul/base/src/nsXULTreeOuterGroupFrame.cpp b/layout/xul/base/src/nsXULTreeOuterGroupFrame.cpp index b5f6ec143720..4cb573778c28 100644 --- a/layout/xul/base/src/nsXULTreeOuterGroupFrame.cpp +++ b/layout/xul/base/src/nsXULTreeOuterGroupFrame.cpp @@ -230,7 +230,7 @@ NS_NewXULTreeOuterGroupFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRB // Constructor nsXULTreeOuterGroupFrame::nsXULTreeOuterGroupFrame(nsIPresShell* aPresShell, PRBool aIsRoot, nsIBoxLayout* aLayoutManager, PRBool aIsHorizontal) :nsXULTreeGroupFrame(aPresShell, aIsRoot, aLayoutManager, aIsHorizontal), - mRowGroupInfo(nsnull), mRowHeight(0), mCurrentIndex(0), + mBatchCount(0), mRowGroupInfo(nsnull), mRowHeight(0), mCurrentIndex(0), mOldIndex(0), mTreeIsSorted(PR_FALSE), mDragOverListener(nsnull), mCanDropBetweenRows(PR_TRUE), mRowHeightWasSet(PR_FALSE), mReflowCallbackPosted(PR_FALSE), mYPosition(0), mScrolling(PR_FALSE), mScrollSmoother(nsnull), mTimePerRow(TIME_PER_ROW_INITAL), mAdjustScroll(PR_FALSE) @@ -545,6 +545,9 @@ nsXULTreeOuterGroupFrame::ScrollbarButtonPressed(PRInt32 aOldIndex, PRInt32 aNew NS_IMETHODIMP nsXULTreeOuterGroupFrame::PositionChanged(PRInt32 aOldIndex, PRInt32& aNewIndex) { + if (mScrolling) + return NS_OK; + PRInt32 oldTwipIndex, newTwipIndex; oldTwipIndex = mCurrentIndex*mRowHeight; newTwipIndex = (aNewIndex*mOnePixel); @@ -627,31 +630,26 @@ nsXULTreeOuterGroupFrame::InternalPositionChangedCallback() } NS_IMETHODIMP -nsXULTreeOuterGroupFrame::InternalPositionChanged(PRBool aUp, PRInt32 aDelta) +nsXULTreeOuterGroupFrame::InternalPositionChanged(PRBool aUp, PRInt32 aDelta, PRBool aForceDestruct) { if (aDelta == 0) return NS_OK; - // begin timing how long it takes to scroll a row - PRTime start = PR_Now(); + // begin timing how long it takes to scroll a row + PRTime start = PR_Now(); - //printf("Actually doing scroll mCurrentIndex=%d, delta=%d!\n", mCurrentIndex, aDelta); + //printf("Actually doing scroll mCurrentIndex=%d, delta=%d!\n", mCurrentIndex, aDelta); - //if (mContentChain) { - // XXX Eventually we need to make the code smart enough to look at a content chain - // when building ANOTHER content chain. - // Ensure all reflows happen first and make sure we're dirty. - nsCOMPtr shell; - mPresContext->GetShell(getter_AddRefs(shell)); - shell->FlushPendingNotifications(); - // } + nsCOMPtr shell; + mPresContext->GetShell(getter_AddRefs(shell)); + shell->FlushPendingNotifications(); PRInt32 visibleRows = 0; if (mRowHeight) visibleRows = GetAvailableHeight()/mRowHeight; // Get our presentation context. - if (aDelta < visibleRows) { + if (aDelta < visibleRows && !aForceDestruct) { PRInt32 loseRows = aDelta; // scrolling down @@ -710,15 +708,16 @@ nsXULTreeOuterGroupFrame::InternalPositionChanged(PRBool aUp, PRInt32 aDelta) mYPosition = mCurrentIndex*mRowHeight; nsBoxLayoutState state(mPresContext); - nsCOMPtr layout; - GetLayoutManager(getter_AddRefs(layout)); - nsTreeLayout* treeLayout = (nsTreeLayout*)layout.get(); - treeLayout->LazyRowCreator(state, this); mScrolling = PR_TRUE; + MarkDirtyChildren(state); shell->FlushPendingNotifications(); mScrolling = PR_FALSE; + VerticalScroll(mYPosition); + if (aForceDestruct) + Redraw(state, nsnull, PR_FALSE); + PRTime end = PR_Now(); PRTime difTime; @@ -1092,6 +1091,10 @@ nsXULTreeOuterGroupFrame::FindNextRowContent(PRInt32& aDelta, nsIContent* aUpwar void nsXULTreeOuterGroupFrame::EnsureRowIsVisible(PRInt32 aRowIndex) { + NS_ASSERTION(aRowIndex >= 0, "Ensure row is visible called with a negative number!"); + if (aRowIndex < 0) + return; + PRInt32 rows = 0; if (mRowHeight) rows = GetAvailableHeight()/mRowHeight; @@ -1106,8 +1109,8 @@ nsXULTreeOuterGroupFrame::EnsureRowIsVisible(PRInt32 aRowIndex) PRBool up = aRowIndex < mCurrentIndex; if (up) { - mCurrentIndex = aRowIndex; delta = mCurrentIndex - aRowIndex; + mCurrentIndex = aRowIndex; } else { // Bring it just into view. @@ -1119,7 +1122,7 @@ nsXULTreeOuterGroupFrame::EnsureRowIsVisible(PRInt32 aRowIndex) } void -nsXULTreeOuterGroupFrame::ScrollToIndex(PRInt32 aRowIndex) +nsXULTreeOuterGroupFrame::ScrollToIndex(PRInt32 aRowIndex, PRBool aForceDestruct) { if (( aRowIndex < 0 ) || (mRowHeight == 0)) return; @@ -1130,11 +1133,14 @@ nsXULTreeOuterGroupFrame::ScrollToIndex(PRInt32 aRowIndex) // 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; mCurrentIndex = newIndex; - InternalPositionChanged(up, delta); + InternalPositionChanged(up, delta, aForceDestruct); // This change has to happen immediately. // Flush any pending reflow commands. @@ -1206,6 +1212,24 @@ nsXULTreeOuterGroupFrame::IndexOfItem(nsIContent* aRoot, nsIContent* aContent, return NS_ERROR_FAILURE; } +NS_IMETHODIMP +nsXULTreeOuterGroupFrame::EndBatch() +{ + NS_ASSERTION(mBatchCount, "EndBatch called on a tree that isn't batching!\n"); + if (mBatchCount == 0) + return NS_OK; + + mBatchCount--; + if (mBatchCount == 0) { + if (mCurrentIndex == mOldIndex) { + nsBoxLayoutState state(mPresContext); + MarkDirtyChildren(state); + } + else ScrollToIndex(mCurrentIndex, PR_TRUE); + } + return NS_OK; +} + NS_IMETHODIMP nsXULTreeOuterGroupFrame::ReflowFinished(nsIPresShell* aPresShell, PRBool* aFlushFlag) { @@ -1251,6 +1275,82 @@ nsXULTreeOuterGroupFrame::ReflowFinished(nsIPresShell* aPresShell, PRBool* aFlus return NS_OK; } +void +nsXULTreeOuterGroupFrame::RegenerateRowGroupInfo(PRBool aOnScreenCount) +{ + NeedsRecalc(); + + PRInt32 oldRowCount = GetRowCount(); + if (mRowGroupInfo) + mRowGroupInfo->Clear(); + PRInt32 newRowCount = GetRowCount(); + + if (mRowHeight <= 0) + return; + + // For removal only, we need to know how many rows are onscreen. These subtract + // from the amount that we need to adjust. For example, if the tree widget is + // scrolled to index 40, and if a folder that is offscreen at index 0 + // is deleted, and it contains 9 kids, then a total of 10 rows are vanishing. + // newRowCount - oldRowCount will be -10 following the removal. However, if 4 of those + // 10 rows were onscreen, then the tree widget's index only needs to be adjusted by + // -6 (-10 + 4). + PRInt32 delta = newRowCount-oldRowCount+aOnScreenCount; + PRInt32 newIndex = mCurrentIndex + delta; + PRBool adjust = PR_FALSE; + + // Check to be sure we're not scrolling off the bottom of the tree + PRInt32 lastPageTopRow = newRowCount - (GetAvailableHeight() / mRowHeight); + if (lastPageTopRow < 0) { + if (aOnScreenCount > 0) + adjust = PR_TRUE; + lastPageTopRow = 0; + } + + if (newIndex > lastPageTopRow) { + newIndex = lastPageTopRow; + if (aOnScreenCount > 0) + adjust = PR_TRUE; + } + + if (newIndex < 0) + newIndex = 0; + + if (!adjust) { + if (mCurrentIndex == 0 || delta == 0) + return; // Just a simple update or we aren't scrolled, so bail. + + nsCOMPtr row; + GetFirstRowContent(getter_AddRefs(row)); + NS_ASSERTION(row, "No row in regen check!"); + if (!row) + return; + + // An element was passed in that was either removed or added. + // We need to adjust our scroll position if this element's index + // is < our current scrolled index. + PRInt32 index = 0; + nsCOMPtr item; + row->GetParent(*getter_AddRefs(item)); + IndexOfItem(mContent, item, PR_FALSE, PR_TRUE, &index); + if (index == -1 || index == mCurrentIndex) + return; + } + + // We mark the outer row group dirty and force a comprehensive + // rebuild of all tree widget frames. This ensures that we + // stay precisely in sync whenever we lose content from above. + if (IsBatching()) + mCurrentIndex = newIndex; + else ScrollToIndex(newIndex, !adjust); + + if (adjust) { + // Force a full redraw. + nsBoxLayoutState state(mPresContext); + Redraw(state, nsnull, PR_FALSE); + } +} + // // Paint // @@ -1261,6 +1361,7 @@ nsXULTreeOuterGroupFrame :: Paint ( nsIPresContext* aPresContext, nsIRenderingCo const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer) { nsresult res = NS_OK; + res = nsBoxFrame::Paint ( aPresContext, aRenderingContext, aDirtyRect, aWhichLayer ); if ( (aWhichLayer == eFramePaintLayer_Content) && diff --git a/layout/xul/base/src/nsXULTreeOuterGroupFrame.h b/layout/xul/base/src/nsXULTreeOuterGroupFrame.h index 53f58e591dde..fc0adbe899e0 100644 --- a/layout/xul/base/src/nsXULTreeOuterGroupFrame.h +++ b/layout/xul/base/src/nsXULTreeOuterGroupFrame.h @@ -130,7 +130,7 @@ public: return mRowHeight; } - void ClearRowGroupInfo() { if (mRowGroupInfo) mRowGroupInfo->Clear(); NeedsRecalc(); }; + void RegenerateRowGroupInfo(PRInt32 aOnscreenCount); void SetRowHeight(PRInt32 aRowHeight); PRBool IsFixedRowSize(); @@ -147,6 +147,9 @@ public: NS_IMETHOD GetRowCount(PRInt32* aResult) { *aResult = GetRowCount(); return NS_OK; } + NS_IMETHOD BeginBatch() { mBatchCount++; mOldIndex = mCurrentIndex; return NS_OK; } + NS_IMETHOD EndBatch(); + NS_IMETHOD PositionChanged(PRInt32 aOldIndex, PRInt32& aNewIndex); NS_IMETHOD ScrollbarButtonPressed(PRInt32 aOldIndex, PRInt32 aNewIndex); NS_IMETHOD VisibilityChanged(PRBool aVisible); @@ -172,14 +175,14 @@ public: // that the row is at the top of the screen (if the row was offscreen to start with). void EnsureRowIsVisible(PRInt32 aRowIndex); - void ScrollToIndex(PRInt32 aRowIndex); + void ScrollToIndex(PRInt32 aRowIndex, PRBool aForceDestruct=PR_FALSE); NS_IMETHOD IndexOfItem(nsIContent* aRoot, nsIContent* aContent, PRBool aDescendIntoRows, // Invariant PRBool aParentIsOpen, PRInt32 *aResult); - NS_IMETHOD InternalPositionChanged(PRBool aUp, PRInt32 aDelta); + NS_IMETHOD InternalPositionChanged(PRBool aUp, PRInt32 aDelta, PRBool aForceDestruct=PR_FALSE); NS_IMETHOD InternalPositionChangedCallback(); NS_IMETHOD Destroy(nsIPresContext* aPresContext); @@ -189,6 +192,7 @@ public: void PostReflowCallback(); + PRBool IsBatching() const { return mBatchCount > 0; }; protected: @@ -203,10 +207,12 @@ protected: nsresult StopScrollTracking(); #endif + PRUint32 mBatchCount; nsXULTreeRowGroupInfo* mRowGroupInfo; PRInt32 mRowHeight; nscoord mOnePixel; PRInt32 mCurrentIndex; // Row-based + PRInt32 mOldIndex; PRPackedBool mTreeIsSorted; PRPackedBool mCanDropBetweenRows; // is the user allowed to drop between rows