Mozilla/mozilla/layout/xul/base/src/nsListBoxLayout.cpp
bzbarsky%mit.edu ba5badedcb Change the FrameNeedsReflow API to pass the dirty flags to be added directly to
the method, instead of setting them before calling the method.  That way we can
avoid reflowing the ancestor of a reflow root which is not itself dirty but has
dirty children.  This also makes it harder to set dirty bits inconsistently
with the FrameNeedsReflow call.  Bug 378784, r+sr=dbaron, pending rbs' review
on the mathml parts.


git-svn-id: svn://10.0.0.236/trunk@226007 18797224-902f-48f8-a5cc-f745e15eee43
2007-05-06 19:16:52 +00:00

272 lines
8.9 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David W. Hyatt (hyatt@netscape.com) (Original Author)
* Joe Hewitt (hewitt@netscape.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsListBoxLayout.h"
#include "nsListBoxBodyFrame.h"
#include "nsIFrame.h"
#include "nsBox.h"
#include "nsBoxLayoutState.h"
#include "nsIScrollableFrame.h"
#include "nsIReflowCallback.h"
#include "nsINameSpaceManager.h"
#include "nsGkAtoms.h"
#include "nsContentUtils.h"
nsListBoxLayout::nsListBoxLayout(nsIPresShell* aPresShell)
: nsGridRowGroupLayout(aPresShell)
{
}
////////// nsIBoxLayout //////////////
NS_IMETHODIMP
nsListBoxLayout::GetPrefSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
{
nsresult rv = nsGridRowGroupLayout::GetPrefSize(aBox, aBoxLayoutState, aSize);
nsListBoxBodyFrame* frame = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
if (frame) {
nscoord rowheight = frame->GetRowHeightAppUnits();
aSize.height = frame->GetRowCount() * rowheight;
// Pad the height.
nscoord y = frame->GetAvailableHeight();
if (aSize.height > y && y > 0 && rowheight > 0) {
nscoord m = (aSize.height-y)%rowheight;
nscoord remainder = m == 0 ? 0 : rowheight - m;
aSize.height += remainder;
}
if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
nsGkAtoms::sizemode)) {
nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState);
if (width > aSize.width)
aSize.width = width;
}
}
return rv;
}
NS_IMETHODIMP
nsListBoxLayout::GetMinSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
{
nsresult rv = nsGridRowGroupLayout::GetMinSize(aBox, aBoxLayoutState, aSize);
nsListBoxBodyFrame* frame = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
if (frame) {
nscoord rowheight = frame->GetRowHeightAppUnits();
aSize.height = frame->GetRowCount() * rowheight;
// Pad the height.
nscoord y = frame->GetAvailableHeight();
if (aSize.height > y && y > 0 && rowheight > 0) {
nscoord m = (aSize.height-y)%rowheight;
nscoord remainder = m == 0 ? 0 : rowheight - m;
aSize.height += remainder;
}
if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
nsGkAtoms::sizemode)) {
nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState);
if (width > aSize.width)
aSize.width = width;
}
}
return rv;
}
NS_IMETHODIMP
nsListBoxLayout::GetMaxSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
{
nsresult rv = nsGridRowGroupLayout::GetMaxSize(aBox, aBoxLayoutState, aSize);
nsListBoxBodyFrame* frame = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
if (frame) {
nscoord rowheight = frame->GetRowHeightAppUnits();
aSize.height = frame->GetRowCount() * rowheight;
// Pad the height.
nscoord y = frame->GetAvailableHeight();
if (aSize.height > y && y > 0 && rowheight > 0) {
nscoord m = (aSize.height-y)%rowheight;
nscoord remainder = m == 0 ? 0 : rowheight - m;
aSize.height += remainder;
}
}
return rv;
}
NS_IMETHODIMP
nsListBoxLayout::Layout(nsIBox* aBox, nsBoxLayoutState& aState)
{
nsListBoxBodyFrame* frame = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
// Always ensure an accurate scrollview position
// This is an edge case that was caused by the row height
// changing after a scroll had occurred. (Bug #51084)
PRInt32 index;
frame->GetIndexOfFirstVisibleRow(&index);
if (index > 0) {
nscoord pos = frame->GetYPosition();
PRInt32 rowHeight = frame->GetRowHeightAppUnits();
if (pos != (rowHeight*index)) {
frame->VerticalScroll(rowHeight*index);
frame->Redraw(aState, nsnull, PR_FALSE);
}
}
nsresult rv = LayoutInternal(aBox, aState);
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
/////////// nsListBoxLayout /////////////////////////
/**
* Called to layout our our children. Does no frame construction
*/
NS_IMETHODIMP
nsListBoxLayout::LayoutInternal(nsIBox* aBox, nsBoxLayoutState& aState)
{
PRInt32 redrawStart = -1;
// Get the start y position.
nsListBoxBodyFrame* body = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
if (!body) {
NS_ERROR("Frame encountered that isn't a listboxbody!\n");
return NS_ERROR_FAILURE;
}
nsMargin margin;
// Get our client rect.
nsRect clientRect;
aBox->GetClientRect(clientRect);
// Get the starting y position and the remaining available
// height.
nscoord availableHeight = body->GetAvailableHeight();
nscoord yOffset = body->GetYPosition();
if (availableHeight <= 0) {
PRBool fixed = (body->GetFixedRowSize() != -1);
if (fixed)
availableHeight = 10;
else
return NS_OK;
}
// run through all our currently created children
nsIBox* box = body->GetChildBox();
// if the reason is resize or initial we must relayout.
nscoord rowHeight = body->GetRowHeightAppUnits();
while (box) {
// If this box is dirty or if it has dirty children, we
// call layout on it.
nsRect childRect(box->GetRect());
box->GetMargin(margin);
// relayout if we must or we are dirty or some of our children are dirty
// or the client area is wider than us
// XXXldb There should probably be a resize check here too!
if (NS_SUBTREE_DIRTY(box) || childRect.width < clientRect.width) {
childRect.x = 0;
childRect.y = yOffset;
childRect.width = clientRect.width;
nsSize size = box->GetPrefSize(aState);
body->SetRowHeight(size.height);
childRect.height = rowHeight;
childRect.Deflate(margin);
box->SetBounds(aState, childRect);
box->Layout(aState);
} else {
// if the child did not need to be relayed out. Then its easy.
// Place the child by just grabbing its rect and adjusting the y.
PRInt32 newPos = yOffset+margin.top;
// are we pushing down or pulling up any rows?
// Then we may have to redraw everything below the moved
// rows.
if (redrawStart == -1 && childRect.y != newPos)
redrawStart = newPos;
childRect.y = newPos;
box->SetBounds(aState, childRect);
}
// Ok now the available size gets smaller and we move the
// starting position of the next child down some.
nscoord size = childRect.height + margin.top + margin.bottom;
yOffset += size;
availableHeight -= size;
box = box->GetNextBox();
}
// 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.
body->PostReflowCallback();
// if rows were pushed down or pulled up because some rows were added
// before them then redraw everything under the inserted rows. The inserted
// rows will automatically be redrawn because the were marked dirty on insertion.
if (redrawStart > -1) {
nsRect bounds(aBox->GetRect());
nsRect tempRect(0,redrawStart,bounds.width, bounds.height - redrawStart);
aBox->Redraw(aState, &tempRect);
}
return NS_OK;
}
// Creation Routines ///////////////////////////////////////////////////////////////////////
nsresult
NS_NewListBoxLayout( nsIPresShell* aPresShell, nsCOMPtr<nsIBoxLayout>& aNewLayout)
{
aNewLayout = new nsListBoxLayout(aPresShell);
return NS_OK;
}