mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
1726 lines
52 KiB
C++
1726 lines
52 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 Hyatt <hyatt@netscape.com>
|
|
* Pierre Phaneuf <pp@ludusdesign.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 ***** */
|
|
|
|
//
|
|
// Eric Vaughan
|
|
// Netscape Communications
|
|
//
|
|
// See documentation in associated header file
|
|
//
|
|
|
|
#include "nsBoxLayoutState.h"
|
|
#include "nsSprocketLayout.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIContent.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsHTMLContainerFrame.h"
|
|
#include "nsBoxFrame.h"
|
|
#include "nsBoxFrame.h"
|
|
|
|
nsIBoxLayout* nsSprocketLayout::gInstance = nsnull;
|
|
|
|
//#define DEBUG_GROW
|
|
|
|
#define DEBUG_SPRING_SIZE 8
|
|
#define DEBUG_BORDER_SIZE 2
|
|
#define COIL_SIZE 8
|
|
|
|
|
|
nsresult
|
|
NS_NewSprocketLayout( nsIPresShell* aPresShell, nsCOMPtr<nsIBoxLayout>& aNewLayout)
|
|
{
|
|
if (!nsSprocketLayout::gInstance) {
|
|
nsSprocketLayout::gInstance = new nsSprocketLayout();
|
|
NS_IF_ADDREF(nsSprocketLayout::gInstance);
|
|
}
|
|
// we have not instance variables so just return our static one.
|
|
aNewLayout = nsSprocketLayout::gInstance;
|
|
return NS_OK;
|
|
}
|
|
|
|
/*static*/ void
|
|
nsSprocketLayout::Shutdown()
|
|
{
|
|
NS_IF_RELEASE(gInstance);
|
|
}
|
|
|
|
nsSprocketLayout::nsSprocketLayout()
|
|
{
|
|
}
|
|
|
|
PRBool
|
|
nsSprocketLayout::IsHorizontal(nsIBox* aBox)
|
|
{
|
|
return (aBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0;
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::GetFrameState(nsIBox* aBox, nsFrameState& aState)
|
|
{
|
|
aState = aBox->GetStateBits();
|
|
}
|
|
|
|
static PRUint8
|
|
GetFrameDirection(nsIBox* aBox)
|
|
{
|
|
return aBox->GetStyleVisibility()->mDirection;
|
|
}
|
|
|
|
static void
|
|
HandleBoxPack(nsIBox* aBox, const nsFrameState& aFrameState, nscoord& aX, nscoord& aY,
|
|
const nsRect& aOriginalRect, const nsRect& aClientRect)
|
|
{
|
|
// In the normal direction we lay out our kids in the positive direction (e.g., |x| will get
|
|
// bigger for a horizontal box, and |y| will get bigger for a vertical box). In the reverse
|
|
// direction, the opposite is true. We'll be laying out each child at a smaller |x| or
|
|
// |y|.
|
|
PRUint8 frameDirection = GetFrameDirection(aBox);
|
|
|
|
if (aFrameState & NS_STATE_IS_HORIZONTAL) {
|
|
if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) {
|
|
// The normal direction. |x| increases as we move through our children.
|
|
aX = aClientRect.x;
|
|
}
|
|
else {
|
|
// The reverse direction. |x| decreases as we move through our children.
|
|
aX = aClientRect.x + aOriginalRect.width;
|
|
}
|
|
// |y| is always in the normal direction in horizontal boxes
|
|
aY = aClientRect.y;
|
|
}
|
|
else {
|
|
// take direction property into account for |x| in vertical boxes
|
|
if (frameDirection == NS_STYLE_DIRECTION_LTR) {
|
|
// The normal direction. |x| increases as we move through our children.
|
|
aX = aClientRect.x;
|
|
}
|
|
else {
|
|
// The reverse direction. |x| decreases as we move through our children.
|
|
aX = aClientRect.x + aOriginalRect.width;
|
|
}
|
|
if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) {
|
|
// The normal direction. |y| increases as we move through our children.
|
|
aY = aClientRect.y;
|
|
}
|
|
else {
|
|
// The reverse direction. |y| decreases as we move through our children.
|
|
aY = aClientRect.y + aOriginalRect.height;
|
|
}
|
|
}
|
|
|
|
// Get our pack/alignment information.
|
|
nsIBox::Halignment halign = aBox->GetHAlign();
|
|
nsIBox::Valignment valign = aBox->GetVAlign();
|
|
|
|
// The following code handles box PACKING. Packing comes into play in the case where the computed size for
|
|
// all of our children (now stored in our client rect) is smaller than the size available for
|
|
// the box (stored in |aOriginalRect|).
|
|
//
|
|
// Here we adjust our |x| and |y| variables accordingly so that we start at the beginning,
|
|
// middle, or end of the box.
|
|
//
|
|
// XXXdwh JUSTIFY needs to be implemented!
|
|
if (aFrameState & NS_STATE_IS_HORIZONTAL) {
|
|
switch(halign) {
|
|
case nsBoxFrame::hAlign_Left:
|
|
break; // Nothing to do. The default initialized us properly.
|
|
|
|
case nsBoxFrame::hAlign_Center:
|
|
if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
aX += (aOriginalRect.width - aClientRect.width)/2;
|
|
else
|
|
aX -= (aOriginalRect.width - aClientRect.width)/2;
|
|
break;
|
|
|
|
case nsBoxFrame::hAlign_Right:
|
|
if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
aX += (aOriginalRect.width - aClientRect.width);
|
|
else
|
|
aX -= (aOriginalRect.width - aClientRect.width);
|
|
break; // Nothing to do for the reverse dir. The default initialized us properly.
|
|
}
|
|
} else {
|
|
switch(valign) {
|
|
case nsBoxFrame::vAlign_Top:
|
|
case nsBoxFrame::vAlign_BaseLine: // This value is technically impossible to specify for pack.
|
|
break; // Don't do anything. We were initialized correctly.
|
|
|
|
case nsBoxFrame::vAlign_Middle:
|
|
if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
aY += (aOriginalRect.height - aClientRect.height)/2;
|
|
else
|
|
aY -= (aOriginalRect.height - aClientRect.height)/2;
|
|
break;
|
|
|
|
case nsBoxFrame::vAlign_Bottom:
|
|
if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
aY += (aOriginalRect.height - aClientRect.height);
|
|
else
|
|
aY -= (aOriginalRect.height - aClientRect.height);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSprocketLayout::Layout(nsIBox* aBox, nsBoxLayoutState& aState)
|
|
{
|
|
// See if we are collapsed. If we are, then simply iterate over all our
|
|
// children and give them a rect of 0 width and height.
|
|
if (aBox->IsCollapsed(aState)) {
|
|
nsIBox* child = aBox->GetChildBox();
|
|
while(child)
|
|
{
|
|
nsBoxFrame::LayoutChildAt(aState, child, nsRect(0,0,0,0));
|
|
child = child->GetNextBox();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
aState.PushStackMemory();
|
|
|
|
// ----- figure out our size ----------
|
|
nsSize originalSize = aBox->GetSize();
|
|
|
|
// -- make sure we remove our border and padding ----
|
|
nsRect clientRect;
|
|
aBox->GetClientRect(clientRect);
|
|
|
|
// |originalClientRect| represents the rect of the entire box (excluding borders
|
|
// and padding). We store it here because we're going to use |clientRect| to hold
|
|
// the required size for all our kids. As an example, consider an hbox with a
|
|
// specified width of 300. If the kids total only 150 pixels of width, then
|
|
// we have 150 pixels left over. |clientRect| is going to hold a width of 150 and
|
|
// is going to be adjusted based off the value of the PACK property. If flexible
|
|
// objects are in the box, then the two rects will match.
|
|
// XXX this should really be called "frameBounds" or something, and we
|
|
// really only need the size
|
|
nsRect originalClientRect(clientRect);
|
|
|
|
// The frame state contains cached knowledge about our box, such as our orientation
|
|
// and direction.
|
|
nsFrameState frameState = 0;
|
|
GetFrameState(aBox, frameState);
|
|
|
|
// Build a list of our children's desired sizes and computed sizes
|
|
nsBoxSize* boxSizes = nsnull;
|
|
nsComputedBoxSize* computedBoxSizes = nsnull;
|
|
|
|
nscoord maxAscent = aBox->GetBoxAscent(aState);
|
|
|
|
nscoord min = 0;
|
|
nscoord max = 0;
|
|
PRInt32 flexes = 0;
|
|
PopulateBoxSizes(aBox, aState, boxSizes, computedBoxSizes, min, max, flexes);
|
|
|
|
// The |size| variable will hold the total size of children along the axis of
|
|
// the box. Continuing with the example begun in the comment above, size would
|
|
// be 150 pixels.
|
|
nscoord size = clientRect.width;
|
|
if (!IsHorizontal(aBox))
|
|
size = clientRect.height;
|
|
ComputeChildSizes(aBox, aState, size, boxSizes, computedBoxSizes);
|
|
|
|
// After the call to ComputeChildSizes, the |size| variable contains the
|
|
// total required size of all the children. We adjust our clientRect in the
|
|
// appropriate dimension to match this size. In our example, we now assign
|
|
// 150 pixels into the clientRect.width.
|
|
//
|
|
// The variables |min| and |max| hold the minimum required size box must be
|
|
// in the OPPOSITE orientation, e.g., for a horizontal box, |min| is the minimum
|
|
// height we require to enclose our children, and |max| is the maximum height
|
|
// required to enclose our children.
|
|
if (IsHorizontal(aBox)) {
|
|
clientRect.width = size;
|
|
if (clientRect.height < min)
|
|
clientRect.height = min;
|
|
|
|
if (frameState & NS_STATE_AUTO_STRETCH) {
|
|
if (clientRect.height > max)
|
|
clientRect.height = max;
|
|
}
|
|
} else {
|
|
clientRect.height = size;
|
|
if (clientRect.width < min)
|
|
clientRect.width = min;
|
|
|
|
if (frameState & NS_STATE_AUTO_STRETCH) {
|
|
if (clientRect.width > max)
|
|
clientRect.width = max;
|
|
}
|
|
}
|
|
|
|
// Grow the frame size now if necessary
|
|
NS_ASSERTION(clientRect.TopLeft() == originalClientRect.TopLeft(),
|
|
"clientRect moved??");
|
|
originalClientRect.UnionRectIncludeEmpty(originalClientRect, clientRect);
|
|
|
|
// With the sizes computed, now it's time to lay out our children.
|
|
PRBool needsRedraw = PR_FALSE;
|
|
PRBool finished;
|
|
nscoord passes = 0;
|
|
|
|
// We flow children at their preferred locations (along with the appropriate computed flex).
|
|
// After we flow a child, it is possible that the child will change its size. If/when this happens,
|
|
// we have to do another pass. Typically only 2 passes are required, but the code is prepared to
|
|
// do as many passes as are necessary to achieve equilibrium.
|
|
nscoord x = 0;
|
|
nscoord y = 0;
|
|
nscoord origX = 0;
|
|
nscoord origY = 0;
|
|
|
|
// |passes| stores our number of passes. If for any reason we end up doing more than, say, 10
|
|
// passes, we assert to indicate that something is seriously screwed up.
|
|
passes = 0;
|
|
do
|
|
{
|
|
#ifdef DEBUG_REFLOW
|
|
if (passes > 0) {
|
|
AddIndents();
|
|
printf("ChildResized doing pass: %d\n", passes);
|
|
}
|
|
#endif
|
|
|
|
// Always assume that we're done. This will change if, for example, children don't stay
|
|
// the same size after being flowed.
|
|
finished = PR_TRUE;
|
|
|
|
// Handle box packing.
|
|
HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect);
|
|
|
|
// Now that packing is taken care of we set up a few additional
|
|
// tracking variables.
|
|
origX = x;
|
|
origY = y;
|
|
|
|
nscoord nextX = x;
|
|
nscoord nextY = y;
|
|
|
|
// Now we iterate over our box children and our box size lists in
|
|
// parallel. For each child, we look at its sizes and figure out
|
|
// where to place it.
|
|
nsComputedBoxSize* childComputedBoxSize = computedBoxSizes;
|
|
nsBoxSize* childBoxSize = boxSizes;
|
|
|
|
nsIBox* child = aBox->GetChildBox();
|
|
|
|
PRInt32 count = 0;
|
|
while (child || (childBoxSize && childBoxSize->bogus))
|
|
{
|
|
// If for some reason, our lists are not the same length, we guard
|
|
// by bailing out of the loop.
|
|
if (childBoxSize == nsnull) {
|
|
NS_NOTREACHED("Lists not the same length.");
|
|
break;
|
|
}
|
|
|
|
nscoord width = clientRect.width;
|
|
nscoord height = clientRect.height;
|
|
|
|
if (!childBoxSize->bogus) {
|
|
// We have a valid box size entry. This entry already contains information about our
|
|
// sizes along the axis of the box (e.g., widths in a horizontal box). If our default
|
|
// ALIGN is not stretch, however, then we also need to know the child's size along the
|
|
// opposite axis.
|
|
if (!(frameState & NS_STATE_AUTO_STRETCH)) {
|
|
nsSize prefSize = child->GetPrefSize(aState);
|
|
nsSize minSize = child->GetMinSize(aState);
|
|
nsSize maxSize = child->GetMaxSize(aState);
|
|
prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize);
|
|
|
|
AddMargin(child, prefSize);
|
|
width = PR_MIN(prefSize.width, originalClientRect.width);
|
|
height = PR_MIN(prefSize.height, originalClientRect.height);
|
|
}
|
|
}
|
|
|
|
// Obtain the computed size along the axis of the box for this child from the computedBoxSize entry.
|
|
// We store the result in |width| for horizontal boxes and |height| for vertical boxes.
|
|
if (frameState & NS_STATE_IS_HORIZONTAL)
|
|
width = childComputedBoxSize->size;
|
|
else
|
|
height = childComputedBoxSize->size;
|
|
|
|
// Adjust our x/y for the left/right spacing.
|
|
if (frameState & NS_STATE_IS_HORIZONTAL) {
|
|
if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
x += (childBoxSize->left);
|
|
else
|
|
x -= (childBoxSize->right);
|
|
} else {
|
|
if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
y += (childBoxSize->left);
|
|
else
|
|
y -= (childBoxSize->right);
|
|
}
|
|
|
|
nextX = x;
|
|
nextY = y;
|
|
|
|
// Now we build a child rect.
|
|
nscoord rectX = x;
|
|
nscoord rectY = y;
|
|
if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) {
|
|
if (frameState & NS_STATE_IS_HORIZONTAL)
|
|
rectX -= width;
|
|
else
|
|
rectY -= height;
|
|
}
|
|
|
|
// We now create an accurate child rect based off our computed size information.
|
|
nsRect childRect(rectX, rectY, width, height);
|
|
|
|
// Sanity check against our clientRect. It is possible that a child specified
|
|
// a size that is too large to fit. If that happens, then we have to grow
|
|
// our client rect. Remember, clientRect is not the total rect of the enclosing
|
|
// box. It currently holds our perception of how big the children needed to
|
|
// be.
|
|
if (childRect.width > clientRect.width || childRect.height > clientRect.height) {
|
|
if (childRect.width > clientRect.width)
|
|
clientRect.width = childRect.width;
|
|
|
|
if (childRect.height > clientRect.height)
|
|
clientRect.height = childRect.height;
|
|
}
|
|
|
|
// |x|, |y|, |nextX|, and |nextY| are updated by this function call. This call
|
|
// also deals with box ALIGNMENT (when stretching is not turned on).
|
|
ComputeChildsNextPosition(aBox, child,
|
|
x,
|
|
y,
|
|
nextX,
|
|
nextY,
|
|
childRect,
|
|
originalClientRect,
|
|
childBoxSize->ascent,
|
|
maxAscent);
|
|
|
|
// Now we update our nextX/Y along our axis and we update our x/y along the opposite
|
|
// axis (since a non-stretching alignment could have caused an adjustment).
|
|
if (frameState & NS_STATE_IS_HORIZONTAL) {
|
|
if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
nextX += (childBoxSize->right);
|
|
else
|
|
nextX -= (childBoxSize->left);
|
|
childRect.y = y;
|
|
}
|
|
else {
|
|
if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
nextY += (childBoxSize->right);
|
|
else
|
|
nextY -= (childBoxSize->left);
|
|
childRect.x = x;
|
|
}
|
|
|
|
// If we encounter a completely bogus box size, we just leave this child completely
|
|
// alone and continue through the loop to the next child.
|
|
if (childBoxSize->bogus)
|
|
{
|
|
childComputedBoxSize = childComputedBoxSize->next;
|
|
childBoxSize = childBoxSize->next;
|
|
count++;
|
|
x = nextX;
|
|
y = nextY;
|
|
continue;
|
|
}
|
|
|
|
nsMargin margin(0,0,0,0);
|
|
|
|
PRBool layout = PR_TRUE;
|
|
|
|
// Deflate the rect of our child by its margin.
|
|
child->GetMargin(margin);
|
|
childRect.Deflate(margin);
|
|
if (childRect.width < 0)
|
|
childRect.width = 0;
|
|
if (childRect.height < 0)
|
|
childRect.height = 0;
|
|
|
|
// Now we're trying to figure out if we have to lay out this child, i.e., to call
|
|
// the child's Layout method.
|
|
if (passes > 0) {
|
|
layout = PR_FALSE;
|
|
} else {
|
|
// Always perform layout if we are dirty or have dirty children
|
|
if (!NS_SUBTREE_DIRTY(child))
|
|
layout = PR_FALSE;
|
|
}
|
|
|
|
// We computed a childRect. Now we want to set the bounds of the child to be that rect.
|
|
// If our old rect is different, then we know our size changed and we cache that fact
|
|
// in the |sizeChanged| variable.
|
|
nsRect oldRect(child->GetRect());
|
|
PRBool sizeChanged = PR_FALSE;
|
|
|
|
child->SetBounds(aState, childRect);
|
|
sizeChanged = (childRect.width != oldRect.width || childRect.height != oldRect.height);
|
|
|
|
PRBool possibleRedraw = PR_FALSE;
|
|
|
|
if (sizeChanged) {
|
|
// Our size is different. Sanity check against our maximum allowed size to ensure
|
|
// we didn't exceed it.
|
|
nsSize minSize = child->GetMinSize(aState);
|
|
nsSize maxSize = child->GetMaxSize(aState);
|
|
maxSize = nsBox::BoundsCheckMinMax(minSize, maxSize);
|
|
|
|
// make sure the size is in our max size.
|
|
if (childRect.width > maxSize.width)
|
|
childRect.width = maxSize.width;
|
|
|
|
if (childRect.height > maxSize.height)
|
|
childRect.height = maxSize.height;
|
|
|
|
// set it again
|
|
child->SetBounds(aState, childRect);
|
|
|
|
// Since the child changed size, we know a redraw is probably going to be required.
|
|
possibleRedraw = PR_TRUE;
|
|
}
|
|
|
|
// if something moved then we might need to redraw
|
|
if (oldRect.x != childRect.x || oldRect.y != childRect.y)
|
|
possibleRedraw = PR_TRUE;
|
|
|
|
// If we already determined that layout was required or if our size has changed, then
|
|
// we make sure to call layout on the child, since its children may need to be shifted
|
|
// around as a result of the size change.
|
|
if (layout || sizeChanged)
|
|
child->Layout(aState);
|
|
|
|
// If the child was a block or inline (e.g., HTML) it may have changed its rect *during* layout.
|
|
// We have to check for this.
|
|
nsRect newChildRect(child->GetRect());
|
|
|
|
if (newChildRect != childRect) {
|
|
#ifdef DEBUG_GROW
|
|
child->DumpBox(stdout);
|
|
printf(" GREW from (%d,%d) -> (%d,%d)\n", childRect.width, childRect.height, newChildRect.width, newChildRect.height);
|
|
#endif
|
|
newChildRect.Inflate(margin);
|
|
childRect.Inflate(margin);
|
|
|
|
// The child changed size during layout. The ChildResized method handles this
|
|
// scenario.
|
|
ChildResized(aBox,
|
|
aState,
|
|
child,
|
|
childBoxSize,
|
|
childComputedBoxSize,
|
|
boxSizes,
|
|
computedBoxSizes,
|
|
childRect,
|
|
newChildRect,
|
|
clientRect,
|
|
flexes,
|
|
finished);
|
|
|
|
// Now that a child resized, it's entirely possible that OUR rect is too small. Now we
|
|
// ensure that |originalClientRect| is grown to accommodate the size of |clientRect|.
|
|
if (clientRect.width > originalClientRect.width || clientRect.height > originalClientRect.height) {
|
|
if (clientRect.width > originalClientRect.width)
|
|
originalClientRect.width = clientRect.width;
|
|
|
|
if (clientRect.height > originalClientRect.height)
|
|
originalClientRect.height = clientRect.height;
|
|
}
|
|
|
|
if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) {
|
|
// Our childRect had its XMost() or YMost() (depending on our layout
|
|
// direction), positioned at a certain point. Ensure that the
|
|
// newChildRect satisfies the same constraint. Note that this is
|
|
// just equivalent to adjusting the x/y by the difference in
|
|
// width/height between childRect and newChildRect. So we don't need
|
|
// to reaccount for the left and right of the box layout state again.
|
|
if (frameState & NS_STATE_IS_HORIZONTAL)
|
|
newChildRect.x = childRect.XMost() - newChildRect.width;
|
|
else
|
|
newChildRect.y = childRect.YMost() - newChildRect.height;
|
|
}
|
|
|
|
// If the child resized then recompute its position.
|
|
ComputeChildsNextPosition(aBox, child,
|
|
x,
|
|
y,
|
|
nextX,
|
|
nextY,
|
|
newChildRect,
|
|
originalClientRect,
|
|
childBoxSize->ascent,
|
|
maxAscent);
|
|
|
|
// Only update the variable in the opposite axis (since this is only here to deal with
|
|
// a non-stretching ALIGNMENT)
|
|
if (frameState & NS_STATE_IS_HORIZONTAL)
|
|
newChildRect.y = y;
|
|
else
|
|
newChildRect.x = x;
|
|
|
|
if (newChildRect.width >= margin.left + margin.right && newChildRect.height >= margin.top + margin.bottom)
|
|
newChildRect.Deflate(margin);
|
|
|
|
if (childRect.width >= margin.left + margin.right && childRect.height >= margin.top + margin.bottom)
|
|
childRect.Deflate(margin);
|
|
|
|
child->SetBounds(aState, newChildRect);
|
|
|
|
// If we are the first box that changed size, then we don't need to do a second pass
|
|
if (count == 0)
|
|
finished = PR_TRUE;
|
|
}
|
|
|
|
// Now update our x/y finally.
|
|
x = nextX;
|
|
y = nextY;
|
|
|
|
// If we get here and |possibleRedraw| is still set, then it's official. We do need a repaint.
|
|
if (possibleRedraw)
|
|
needsRedraw = PR_TRUE;
|
|
|
|
// Move to the next child.
|
|
childComputedBoxSize = childComputedBoxSize->next;
|
|
childBoxSize = childBoxSize->next;
|
|
|
|
child = child->GetNextBox();
|
|
count++;
|
|
}
|
|
|
|
// Sanity-checking code to ensure we don't do an infinite # of passes.
|
|
passes++;
|
|
NS_ASSERTION(passes < 10, "A Box's child is constantly growing!!!!!");
|
|
if (passes > 10)
|
|
break;
|
|
} while (PR_FALSE == finished);
|
|
|
|
// Get rid of our size lists.
|
|
while(boxSizes)
|
|
{
|
|
nsBoxSize* toDelete = boxSizes;
|
|
boxSizes = boxSizes->next;
|
|
delete toDelete;
|
|
}
|
|
|
|
while(computedBoxSizes)
|
|
{
|
|
nsComputedBoxSize* toDelete = computedBoxSizes;
|
|
computedBoxSizes = computedBoxSizes->next;
|
|
delete toDelete;
|
|
}
|
|
|
|
// See if one of our children forced us to get bigger
|
|
nsRect tmpClientRect(originalClientRect);
|
|
nsMargin bp(0,0,0,0);
|
|
aBox->GetBorderAndPadding(bp);
|
|
tmpClientRect.Inflate(bp);
|
|
|
|
if (tmpClientRect.width > originalSize.width || tmpClientRect.height > originalSize.height)
|
|
{
|
|
// if it did reset our bounds.
|
|
nsRect bounds(aBox->GetRect());
|
|
if (tmpClientRect.width > originalSize.width)
|
|
bounds.width = tmpClientRect.width;
|
|
|
|
if (tmpClientRect.height > originalSize.height)
|
|
bounds.height = tmpClientRect.height;
|
|
|
|
aBox->SetBounds(aState, bounds);
|
|
}
|
|
|
|
// Because our size grew, we now have to readjust because of box packing. Repack
|
|
// in order to update our x and y to the correct values.
|
|
HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect);
|
|
|
|
// Compare against our original x and y and only worry about adjusting the children if
|
|
// we really did have to change the positions because of packing (typically for 'center'
|
|
// or 'end' pack values).
|
|
if (x != origX || y != origY) {
|
|
nsIBox* child = aBox->GetChildBox();
|
|
|
|
// reposition all our children
|
|
while (child)
|
|
{
|
|
nsRect childRect(child->GetRect());
|
|
childRect.x += (x - origX);
|
|
childRect.y += (y - origY);
|
|
child->SetBounds(aState, childRect);
|
|
child = child->GetNextBox();
|
|
}
|
|
}
|
|
|
|
// Now do our redraw.
|
|
if (needsRedraw)
|
|
aBox->Redraw(aState);
|
|
|
|
aState.PopStackMemory();
|
|
|
|
// That's it! If you made it this far without having a nervous breakdown,
|
|
// congratulations! Go get yourself a beer.
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::PopulateBoxSizes(nsIBox* aBox, nsBoxLayoutState& aState, nsBoxSize*& aBoxSizes, nsComputedBoxSize*& aComputedBoxSizes, nscoord& aMinSize, nscoord& aMaxSize, PRInt32& aFlexes)
|
|
{
|
|
// used for the equal size flag
|
|
nscoord biggestPrefWidth = 0;
|
|
nscoord biggestMinWidth = 0;
|
|
nscoord smallestMaxWidth = NS_INTRINSICSIZE;
|
|
|
|
nsFrameState frameState = 0;
|
|
GetFrameState(aBox, frameState);
|
|
|
|
//if (frameState & NS_STATE_CURRENTLY_IN_DEBUG)
|
|
// printf("In debug\n");
|
|
|
|
aMinSize = 0;
|
|
aMaxSize = NS_INTRINSICSIZE;
|
|
|
|
PRBool isHorizontal;
|
|
|
|
if (IsHorizontal(aBox))
|
|
isHorizontal = PR_TRUE;
|
|
else
|
|
isHorizontal = PR_FALSE;
|
|
|
|
// this is a nice little optimization
|
|
// it turns out that if we only have 1 flexable child
|
|
// then it does not matter what its preferred size is
|
|
// there is nothing to flex it relative. This is great
|
|
// because we can avoid asking for a preferred size in this
|
|
// case. Why is this good? Well you might have html inside it
|
|
// and asking html for its preferred size is rather expensive.
|
|
// so we can just optimize it out this way.
|
|
|
|
// set flexes
|
|
nsIBox* child = aBox->GetChildBox();
|
|
|
|
aFlexes = 0;
|
|
nsBoxSize* currentBox = nsnull;
|
|
|
|
#if 0
|
|
nsBoxSize* start = aBoxSizes;
|
|
|
|
while(child)
|
|
{
|
|
// ok if we started with a list move down the list
|
|
// until we reach the end. Then start looking at childen.
|
|
// This feature is used extensively for Grid.
|
|
nscoord flex = 0;
|
|
|
|
if (!start) {
|
|
if (!currentBox) {
|
|
aBoxSizes = new (aState) nsBoxSize();
|
|
currentBox = aBoxSizes;
|
|
} else {
|
|
currentBox->next = new (aState) nsBoxSize();
|
|
currentBox = currentBox->next;
|
|
}
|
|
|
|
|
|
flex = child->GetFlex(aState);
|
|
|
|
currentBox->flex = flex;
|
|
currentBox->collapsed = child->IsCollapsed(aState);
|
|
} else {
|
|
flex = start->flex;
|
|
start = start->next;
|
|
}
|
|
|
|
if (flex > 0)
|
|
aFlexes++;
|
|
|
|
child = child->GetNextBox();
|
|
}
|
|
#endif
|
|
|
|
// get pref, min, max
|
|
child = aBox->GetChildBox();
|
|
currentBox = aBoxSizes;
|
|
nsBoxSize* last = nsnull;
|
|
|
|
nscoord maxFlex = 0;
|
|
PRInt32 childCount = 0;
|
|
|
|
while(child)
|
|
{
|
|
while (currentBox && currentBox->bogus) {
|
|
last = currentBox;
|
|
currentBox = currentBox->next;
|
|
}
|
|
++childCount;
|
|
nsSize pref(0,0);
|
|
nsSize minSize(0,0);
|
|
nsSize maxSize(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
|
|
nscoord ascent = 0;
|
|
PRBool collapsed = child->IsCollapsed(aState);
|
|
|
|
if (!collapsed) {
|
|
// only one flexible child? Cool we will just make its preferred size
|
|
// 0 then and not even have to ask for it.
|
|
//if (flexes != 1) {
|
|
|
|
pref = child->GetPrefSize(aState);
|
|
minSize = child->GetMinSize(aState);
|
|
maxSize = nsBox::BoundsCheckMinMax(minSize, child->GetMaxSize(aState));
|
|
ascent = child->GetBoxAscent(aState);
|
|
nsMargin margin;
|
|
child->GetMargin(margin);
|
|
ascent += margin.top;
|
|
//}
|
|
|
|
pref = nsBox::BoundsCheck(minSize, pref, maxSize);
|
|
|
|
AddMargin(child, pref);
|
|
AddMargin(child, minSize);
|
|
AddMargin(child, maxSize);
|
|
}
|
|
|
|
if (!currentBox) {
|
|
// create one.
|
|
currentBox = new (aState) nsBoxSize();
|
|
if (!aBoxSizes) {
|
|
aBoxSizes = currentBox;
|
|
last = aBoxSizes;
|
|
} else {
|
|
last->next = currentBox;
|
|
last = currentBox;
|
|
}
|
|
|
|
nscoord minWidth;
|
|
nscoord maxWidth;
|
|
nscoord prefWidth;
|
|
|
|
// get sizes from child
|
|
if (isHorizontal) {
|
|
minWidth = minSize.width;
|
|
maxWidth = maxSize.width;
|
|
prefWidth = pref.width;
|
|
} else {
|
|
minWidth = minSize.height;
|
|
maxWidth = maxSize.height;
|
|
prefWidth = pref.height;
|
|
}
|
|
|
|
nscoord flex = child->GetFlex(aState);
|
|
|
|
// set them if you collapsed you are not flexible.
|
|
if (collapsed) {
|
|
currentBox->flex = 0;
|
|
}
|
|
else {
|
|
if (flex > maxFlex) {
|
|
maxFlex = flex;
|
|
}
|
|
currentBox->flex = flex;
|
|
}
|
|
|
|
// we specified all our children are equal size;
|
|
if (frameState & NS_STATE_EQUAL_SIZE) {
|
|
|
|
if (prefWidth > biggestPrefWidth)
|
|
biggestPrefWidth = prefWidth;
|
|
|
|
if (minWidth > biggestMinWidth)
|
|
biggestMinWidth = minWidth;
|
|
|
|
if (maxWidth < smallestMaxWidth)
|
|
smallestMaxWidth = maxWidth;
|
|
} else { // not we can set our children right now.
|
|
currentBox->pref = prefWidth;
|
|
currentBox->min = minWidth;
|
|
currentBox->max = maxWidth;
|
|
}
|
|
|
|
NS_ASSERTION(minWidth <= prefWidth && prefWidth <= maxWidth,"Bad min, pref, max widths!");
|
|
|
|
}
|
|
|
|
if (!isHorizontal) {
|
|
if (minSize.width > aMinSize)
|
|
aMinSize = minSize.width;
|
|
|
|
if (maxSize.width < aMaxSize)
|
|
aMaxSize = maxSize.width;
|
|
|
|
} else {
|
|
if (minSize.height > aMinSize)
|
|
aMinSize = minSize.height;
|
|
|
|
if (maxSize.height < aMaxSize)
|
|
aMaxSize = maxSize.height;
|
|
}
|
|
|
|
currentBox->ascent = ascent;
|
|
currentBox->collapsed = collapsed;
|
|
aFlexes += currentBox->flex;
|
|
|
|
child = child->GetNextBox();
|
|
|
|
last = currentBox;
|
|
currentBox = currentBox->next;
|
|
|
|
}
|
|
|
|
if (childCount > 0) {
|
|
nscoord maxAllowedFlex = nscoord_MAX / childCount;
|
|
|
|
if (NS_UNLIKELY(maxFlex > maxAllowedFlex)) {
|
|
// clamp all the flexes
|
|
currentBox = aBoxSizes;
|
|
while (currentBox) {
|
|
currentBox->flex = PR_MIN(currentBox->flex, maxAllowedFlex);
|
|
currentBox = currentBox->next;
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
else {
|
|
NS_ASSERTION(maxFlex == 0, "How did that happen?");
|
|
}
|
|
#endif
|
|
|
|
// we specified all our children are equal size;
|
|
if (frameState & NS_STATE_EQUAL_SIZE) {
|
|
smallestMaxWidth = PR_MAX(smallestMaxWidth, biggestMinWidth);
|
|
biggestPrefWidth = nsBox::BoundsCheck(biggestMinWidth, biggestPrefWidth, smallestMaxWidth);
|
|
|
|
currentBox = aBoxSizes;
|
|
|
|
while(currentBox)
|
|
{
|
|
if (!currentBox->collapsed) {
|
|
currentBox->pref = biggestPrefWidth;
|
|
currentBox->min = biggestMinWidth;
|
|
currentBox->max = smallestMaxWidth;
|
|
} else {
|
|
currentBox->pref = 0;
|
|
currentBox->min = 0;
|
|
currentBox->max = 0;
|
|
}
|
|
currentBox = currentBox->next;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::ComputeChildsNextPosition(nsIBox* aBox,
|
|
nsIBox* aChild,
|
|
nscoord& aCurX,
|
|
nscoord& aCurY,
|
|
nscoord& aNextX,
|
|
nscoord& aNextY,
|
|
const nsRect& aCurrentChildSize,
|
|
const nsRect& aBoxRect,
|
|
nscoord childAscent,
|
|
nscoord aMaxAscent)
|
|
{
|
|
nsFrameState frameState = 0;
|
|
GetFrameState(aBox, frameState);
|
|
|
|
nsIBox::Halignment halign = aBox->GetHAlign();
|
|
nsIBox::Valignment valign = aBox->GetVAlign();
|
|
|
|
if (IsHorizontal(aBox)) {
|
|
// Handle alignment of a horizontal box's children.
|
|
if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
aNextX = aCurX + aCurrentChildSize.width;
|
|
else aNextX = aCurX - aCurrentChildSize.width;
|
|
|
|
if (frameState & NS_STATE_AUTO_STRETCH)
|
|
aCurY = aBoxRect.y;
|
|
else {
|
|
switch (valign)
|
|
{
|
|
case nsBoxFrame::vAlign_BaseLine:
|
|
aCurY = aBoxRect.y + (aMaxAscent - childAscent);
|
|
break;
|
|
|
|
case nsBoxFrame::vAlign_Top:
|
|
aCurY = aBoxRect.y;
|
|
break;
|
|
case nsBoxFrame::vAlign_Middle:
|
|
aCurY = aBoxRect.y + (aBoxRect.height/2 - aCurrentChildSize.height/2);
|
|
break;
|
|
case nsBoxFrame::vAlign_Bottom:
|
|
aCurY = aBoxRect.y + aBoxRect.height - aCurrentChildSize.height;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// Handle alignment of a vertical box's children.
|
|
if (frameState & NS_STATE_IS_DIRECTION_NORMAL)
|
|
aNextY = aCurY + aCurrentChildSize.height;
|
|
else
|
|
aNextY = aCurY - aCurrentChildSize.height;
|
|
|
|
if (frameState & NS_STATE_AUTO_STRETCH)
|
|
aCurX = aBoxRect.x;
|
|
else {
|
|
PRUint8 frameDirection = GetFrameDirection(aBox);
|
|
switch (halign)
|
|
{
|
|
case nsBoxFrame::hAlign_Left:
|
|
if (frameDirection == NS_STYLE_DIRECTION_LTR)
|
|
aCurX = aBoxRect.x;
|
|
else
|
|
aCurX = aBoxRect.x + aBoxRect.width - aCurrentChildSize.width;
|
|
break;
|
|
case nsBoxFrame::hAlign_Center:
|
|
aCurX = aBoxRect.x + (aBoxRect.width/2 - aCurrentChildSize.width/2);
|
|
break;
|
|
case nsBoxFrame::hAlign_Right:
|
|
if (frameDirection == NS_STYLE_DIRECTION_LTR)
|
|
aCurX = aBoxRect.x + aBoxRect.width - aCurrentChildSize.width;
|
|
else
|
|
aCurX = aBoxRect.x;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::ChildResized(nsIBox* aBox,
|
|
nsBoxLayoutState& aState,
|
|
nsIBox* aChild,
|
|
nsBoxSize* aChildBoxSize,
|
|
nsComputedBoxSize* aChildComputedSize,
|
|
nsBoxSize* aBoxSizes,
|
|
nsComputedBoxSize* aComputedBoxSizes,
|
|
const nsRect& aChildLayoutRect,
|
|
nsRect& aChildActualRect,
|
|
nsRect& aContainingRect,
|
|
PRInt32 aFlexes,
|
|
PRBool& aFinished)
|
|
|
|
{
|
|
nsRect childCurrentRect(aChildLayoutRect);
|
|
|
|
PRBool isHorizontal = IsHorizontal(aBox);
|
|
nscoord childLayoutWidth = GET_WIDTH(aChildLayoutRect,isHorizontal);
|
|
nscoord& childActualWidth = GET_WIDTH(aChildActualRect,isHorizontal);
|
|
nscoord& containingWidth = GET_WIDTH(aContainingRect,isHorizontal);
|
|
|
|
//nscoord childLayoutHeight = GET_HEIGHT(aChildLayoutRect,isHorizontal);
|
|
nscoord& childActualHeight = GET_HEIGHT(aChildActualRect,isHorizontal);
|
|
nscoord& containingHeight = GET_HEIGHT(aContainingRect,isHorizontal);
|
|
|
|
PRBool recompute = PR_FALSE;
|
|
|
|
// if we are a horizontal box see if the child will fit inside us.
|
|
if ( childActualHeight > containingHeight) {
|
|
// if we are a horizontal box and the child is bigger than our height
|
|
|
|
// ok if the height changed then we need to reflow everyone but us at the new height
|
|
// so we will set the changed index to be us. And signal that we need a new pass.
|
|
|
|
nsSize min = aChild->GetMinSize(aState);
|
|
nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState));
|
|
AddMargin(aChild, max);
|
|
|
|
if (isHorizontal)
|
|
childActualHeight = max.height < childActualHeight ? max.height : childActualHeight;
|
|
else
|
|
childActualHeight = max.width < childActualHeight ? max.width : childActualHeight;
|
|
|
|
// only set if it changes
|
|
if (childActualHeight > containingHeight) {
|
|
containingHeight = childActualHeight;
|
|
|
|
// remember we do not need to clear the resized list because changing the height of a horizontal box
|
|
// will not affect the width of any of its children because block flow left to right, top to bottom. Just trust me
|
|
// on this one.
|
|
aFinished = PR_FALSE;
|
|
|
|
// only recompute if there are flexes.
|
|
if (aFlexes > 0) {
|
|
// relayout everything
|
|
recompute = PR_TRUE;
|
|
InvalidateComputedSizes(aComputedBoxSizes);
|
|
nsComputedBoxSize* node = aComputedBoxSizes;
|
|
|
|
while(node) {
|
|
node->resized = PR_FALSE;
|
|
node = node->next;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (childActualWidth > childLayoutWidth) {
|
|
nsSize min = aChild->GetMinSize(aState);
|
|
nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState));
|
|
|
|
AddMargin(aChild, max);
|
|
|
|
// our width now becomes the new size
|
|
|
|
if (isHorizontal)
|
|
childActualWidth = max.width < childActualWidth ? max.width : childActualWidth;
|
|
else
|
|
childActualWidth = max.height < childActualWidth ? max.height : childActualWidth;
|
|
|
|
if (childActualWidth > childLayoutWidth) {
|
|
aChildComputedSize->size = childActualWidth;
|
|
aChildBoxSize->min = childActualWidth;
|
|
if (aChildBoxSize->pref < childActualWidth)
|
|
aChildBoxSize->pref = childActualWidth;
|
|
if (aChildBoxSize->max < childActualWidth)
|
|
aChildBoxSize->max = childActualWidth;
|
|
|
|
// if we have flexible elements with us then reflex things. Otherwise we can skip doing it.
|
|
if (aFlexes > 0) {
|
|
InvalidateComputedSizes(aComputedBoxSizes);
|
|
|
|
nsComputedBoxSize* node = aComputedBoxSizes;
|
|
aChildComputedSize->resized = PR_TRUE;
|
|
|
|
while(node) {
|
|
if (node->resized)
|
|
node->valid = PR_TRUE;
|
|
|
|
node = node->next;
|
|
}
|
|
|
|
recompute = PR_TRUE;
|
|
aFinished = PR_FALSE;
|
|
} else {
|
|
containingWidth += aChildComputedSize->size - childLayoutWidth;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (recompute)
|
|
ComputeChildSizes(aBox, aState, containingWidth, aBoxSizes, aComputedBoxSizes);
|
|
|
|
if (childCurrentRect != aChildActualRect) {
|
|
// the childRect includes the margin
|
|
// make sure we remove it before setting
|
|
// the bounds.
|
|
nsMargin margin(0,0,0,0);
|
|
aChild->GetMargin(margin);
|
|
nsRect rect(aChildActualRect);
|
|
if (rect.width >= margin.left + margin.right && rect.height >= margin.top + margin.bottom)
|
|
rect.Deflate(margin);
|
|
|
|
aChild->SetBounds(aState, rect);
|
|
aChild->Layout(aState);
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::InvalidateComputedSizes(nsComputedBoxSize* aComputedBoxSizes)
|
|
{
|
|
while(aComputedBoxSizes) {
|
|
aComputedBoxSizes->valid = PR_FALSE;
|
|
aComputedBoxSizes = aComputedBoxSizes->next;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::ComputeChildSizes(nsIBox* aBox,
|
|
nsBoxLayoutState& aState,
|
|
nscoord& aGivenSize,
|
|
nsBoxSize* aBoxSizes,
|
|
nsComputedBoxSize*& aComputedBoxSizes)
|
|
{
|
|
|
|
//nscoord onePixel = aState.PresContext()->IntScaledPixelsToTwips(1);
|
|
|
|
PRInt32 sizeRemaining = aGivenSize;
|
|
PRInt32 spacerConstantsRemaining = 0;
|
|
|
|
// ----- calculate the spacers constants and the size remaining -----
|
|
|
|
if (!aComputedBoxSizes)
|
|
aComputedBoxSizes = new (aState) nsComputedBoxSize();
|
|
|
|
nsBoxSize* boxSizes = aBoxSizes;
|
|
nsComputedBoxSize* computedBoxSizes = aComputedBoxSizes;
|
|
PRInt32 count = 0;
|
|
PRInt32 validCount = 0;
|
|
|
|
while (boxSizes)
|
|
{
|
|
|
|
NS_ASSERTION((boxSizes->min <= boxSizes->pref && boxSizes->pref <= boxSizes->max),"bad pref, min, max size");
|
|
|
|
|
|
// ignore collapsed children
|
|
// if (boxSizes->collapsed)
|
|
// {
|
|
// computedBoxSizes->valid = PR_TRUE;
|
|
// computedBoxSizes->size = boxSizes->pref;
|
|
// validCount++;
|
|
// boxSizes->flex = 0;
|
|
// }// else {
|
|
|
|
if (computedBoxSizes->valid) {
|
|
sizeRemaining -= computedBoxSizes->size;
|
|
validCount++;
|
|
} else {
|
|
if (boxSizes->flex == 0)
|
|
{
|
|
computedBoxSizes->valid = PR_TRUE;
|
|
computedBoxSizes->size = boxSizes->pref;
|
|
validCount++;
|
|
}
|
|
|
|
spacerConstantsRemaining += boxSizes->flex;
|
|
sizeRemaining -= boxSizes->pref;
|
|
}
|
|
|
|
sizeRemaining -= (boxSizes->left + boxSizes->right);
|
|
|
|
//}
|
|
|
|
boxSizes = boxSizes->next;
|
|
|
|
if (boxSizes && !computedBoxSizes->next)
|
|
computedBoxSizes->next = new (aState) nsComputedBoxSize();
|
|
|
|
computedBoxSizes = computedBoxSizes->next;
|
|
count++;
|
|
}
|
|
|
|
// everything accounted for?
|
|
if (validCount < count)
|
|
{
|
|
// ----- Ok we are give a size to fit into so stretch or squeeze to fit
|
|
// ----- Make sure we look at our min and max size
|
|
PRBool limit = PR_TRUE;
|
|
for (int pass=1; PR_TRUE == limit; pass++)
|
|
{
|
|
limit = PR_FALSE;
|
|
boxSizes = aBoxSizes;
|
|
computedBoxSizes = aComputedBoxSizes;
|
|
|
|
while (boxSizes) {
|
|
|
|
// ignore collapsed spacers
|
|
|
|
// if (!boxSizes->collapsed) {
|
|
|
|
nscoord pref = 0;
|
|
nscoord max = NS_INTRINSICSIZE;
|
|
nscoord min = 0;
|
|
nscoord flex = 0;
|
|
|
|
pref = boxSizes->pref;
|
|
min = boxSizes->min;
|
|
max = boxSizes->max;
|
|
flex = boxSizes->flex;
|
|
|
|
// ----- look at our min and max limits make sure we aren't too small or too big -----
|
|
if (!computedBoxSizes->valid) {
|
|
PRInt32 newSize = pref + sizeRemaining*flex/spacerConstantsRemaining; //NSToCoordRound(float((sizeRemaining*flex)/spacerConstantsRemaining));
|
|
|
|
if (newSize<=min) {
|
|
computedBoxSizes->size = min;
|
|
computedBoxSizes->valid = PR_TRUE;
|
|
spacerConstantsRemaining -= flex;
|
|
sizeRemaining += pref;
|
|
sizeRemaining -= min;
|
|
limit = PR_TRUE;
|
|
} else if (newSize>=max) {
|
|
computedBoxSizes->size = max;
|
|
computedBoxSizes->valid = PR_TRUE;
|
|
spacerConstantsRemaining -= flex;
|
|
sizeRemaining += pref;
|
|
sizeRemaining -= max;
|
|
limit = PR_TRUE;
|
|
}
|
|
}
|
|
// }
|
|
boxSizes = boxSizes->next;
|
|
computedBoxSizes = computedBoxSizes->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---- once we have removed and min and max issues just stretch us out in the remaining space
|
|
// ---- or shrink us. Depends on the size remaining and the spacer constants
|
|
aGivenSize = 0;
|
|
boxSizes = aBoxSizes;
|
|
computedBoxSizes = aComputedBoxSizes;
|
|
|
|
while (boxSizes) {
|
|
|
|
// ignore collapsed spacers
|
|
// if (!(boxSizes && boxSizes->collapsed)) {
|
|
|
|
nscoord pref = 0;
|
|
nscoord flex = 0;
|
|
pref = boxSizes->pref;
|
|
flex = boxSizes->flex;
|
|
|
|
if (!computedBoxSizes->valid) {
|
|
computedBoxSizes->size = pref + flex*sizeRemaining/spacerConstantsRemaining; //NSToCoordFloor(float((flex*sizeRemaining)/spacerConstantsRemaining));
|
|
computedBoxSizes->valid = PR_TRUE;
|
|
}
|
|
|
|
aGivenSize += (boxSizes->left + boxSizes->right);
|
|
aGivenSize += computedBoxSizes->size;
|
|
|
|
// }
|
|
|
|
boxSizes = boxSizes->next;
|
|
computedBoxSizes = computedBoxSizes->next;
|
|
}
|
|
}
|
|
|
|
|
|
nsSize
|
|
nsSprocketLayout::GetPrefSize(nsIBox* aBox, nsBoxLayoutState& aState)
|
|
{
|
|
nsSize vpref (0, 0);
|
|
PRBool isHorizontal = IsHorizontal(aBox);
|
|
|
|
nscoord biggestPref = 0;
|
|
|
|
// run through all the children and get their min, max, and preferred sizes
|
|
// return us the size of the box
|
|
|
|
nsIBox* child = aBox->GetChildBox();
|
|
nsFrameState frameState = 0;
|
|
GetFrameState(aBox, frameState);
|
|
PRBool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE);
|
|
PRInt32 count = 0;
|
|
|
|
while (child)
|
|
{
|
|
// ignore collapsed children
|
|
if (!child->IsCollapsed(aState))
|
|
{
|
|
nsSize pref = child->GetPrefSize(aState);
|
|
AddMargin(child, pref);
|
|
|
|
if (isEqual) {
|
|
if (isHorizontal)
|
|
{
|
|
if (pref.width > biggestPref)
|
|
biggestPref = pref.width;
|
|
} else {
|
|
if (pref.height > biggestPref)
|
|
biggestPref = pref.height;
|
|
}
|
|
}
|
|
|
|
AddLargestSize(vpref, pref, isHorizontal);
|
|
count++;
|
|
}
|
|
|
|
child = child->GetNextBox();
|
|
}
|
|
|
|
if (isEqual) {
|
|
if (isHorizontal)
|
|
vpref.width = biggestPref*count;
|
|
else
|
|
vpref.height = biggestPref*count;
|
|
}
|
|
|
|
// now add our border and padding
|
|
AddBorderAndPadding(aBox, vpref);
|
|
|
|
return vpref;
|
|
}
|
|
|
|
nsSize
|
|
nsSprocketLayout::GetMinSize(nsIBox* aBox, nsBoxLayoutState& aState)
|
|
{
|
|
nsSize minSize (0, 0);
|
|
PRBool isHorizontal = IsHorizontal(aBox);
|
|
|
|
nscoord biggestMin = 0;
|
|
|
|
|
|
// run through all the children and get their min, max, and preferred sizes
|
|
// return us the size of the box
|
|
|
|
nsIBox* child = aBox->GetChildBox();
|
|
nsFrameState frameState = 0;
|
|
GetFrameState(aBox, frameState);
|
|
PRBool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE);
|
|
PRInt32 count = 0;
|
|
|
|
while (child)
|
|
{
|
|
// ignore collapsed children
|
|
if (!child->IsCollapsed(aState))
|
|
{
|
|
nsSize min = child->GetMinSize(aState);
|
|
nsSize pref(0,0);
|
|
|
|
// if the child is not flexible then
|
|
// its min size is its pref size.
|
|
if (child->GetFlex(aState) == 0) {
|
|
pref = child->GetPrefSize(aState);
|
|
if (isHorizontal)
|
|
min.width = pref.width;
|
|
else
|
|
min.height = pref.height;
|
|
}
|
|
|
|
if (isEqual) {
|
|
if (isHorizontal)
|
|
{
|
|
if (min.width > biggestMin)
|
|
biggestMin = min.width;
|
|
} else {
|
|
if (min.height > biggestMin)
|
|
biggestMin = min.height;
|
|
}
|
|
}
|
|
|
|
AddMargin(child, min);
|
|
AddLargestSize(minSize, min, isHorizontal);
|
|
count++;
|
|
}
|
|
|
|
child = child->GetNextBox();
|
|
}
|
|
|
|
|
|
if (isEqual) {
|
|
if (isHorizontal)
|
|
minSize.width = biggestMin*count;
|
|
else
|
|
minSize.height = biggestMin*count;
|
|
}
|
|
|
|
// now add our border and padding
|
|
AddBorderAndPadding(aBox, minSize);
|
|
|
|
return minSize;
|
|
}
|
|
|
|
nsSize
|
|
nsSprocketLayout::GetMaxSize(nsIBox* aBox, nsBoxLayoutState& aState)
|
|
{
|
|
|
|
PRBool isHorizontal = IsHorizontal(aBox);
|
|
|
|
nscoord smallestMax = NS_INTRINSICSIZE;
|
|
nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
|
|
|
|
// run through all the children and get their min, max, and preferred sizes
|
|
// return us the size of the box
|
|
|
|
nsIBox* child = aBox->GetChildBox();
|
|
nsFrameState frameState = 0;
|
|
GetFrameState(aBox, frameState);
|
|
PRBool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE);
|
|
PRInt32 count = 0;
|
|
|
|
while (child)
|
|
{
|
|
// ignore collapsed children
|
|
if (!child->IsCollapsed(aState))
|
|
{
|
|
// if completely redefined don't even ask our child for its size.
|
|
nsSize min = child->GetMinSize(aState);
|
|
nsSize max = nsBox::BoundsCheckMinMax(min, child->GetMaxSize(aState));
|
|
|
|
AddMargin(child, max);
|
|
AddSmallestSize(maxSize, max, isHorizontal);
|
|
|
|
if (isEqual) {
|
|
if (isHorizontal)
|
|
{
|
|
if (max.width < smallestMax)
|
|
smallestMax = max.width;
|
|
} else {
|
|
if (max.height < smallestMax)
|
|
smallestMax = max.height;
|
|
}
|
|
}
|
|
count++;
|
|
}
|
|
|
|
child = child->GetNextBox();
|
|
}
|
|
|
|
if (isEqual) {
|
|
if (isHorizontal) {
|
|
if (smallestMax != NS_INTRINSICSIZE)
|
|
maxSize.width = smallestMax*count;
|
|
else
|
|
maxSize.width = NS_INTRINSICSIZE;
|
|
} else {
|
|
if (smallestMax != NS_INTRINSICSIZE)
|
|
maxSize.height = smallestMax*count;
|
|
else
|
|
maxSize.height = NS_INTRINSICSIZE;
|
|
}
|
|
}
|
|
|
|
// now add our border and padding
|
|
AddBorderAndPadding(aBox, maxSize);
|
|
|
|
return maxSize;
|
|
}
|
|
|
|
|
|
nscoord
|
|
nsSprocketLayout::GetAscent(nsIBox* aBox, nsBoxLayoutState& aState)
|
|
{
|
|
nscoord vAscent = 0;
|
|
|
|
PRBool isHorizontal = IsHorizontal(aBox);
|
|
|
|
// run through all the children and get their min, max, and preferred sizes
|
|
// return us the size of the box
|
|
|
|
nsIBox* child = aBox->GetChildBox();
|
|
|
|
while (child)
|
|
{
|
|
// ignore collapsed children
|
|
//if (!child->IsCollapsed(aState))
|
|
//{
|
|
// if completely redefined don't even ask our child for its size.
|
|
nscoord ascent = child->GetBoxAscent(aState);
|
|
|
|
nsMargin margin;
|
|
child->GetMargin(margin);
|
|
ascent += margin.top;
|
|
|
|
if (isHorizontal)
|
|
{
|
|
if (ascent > vAscent)
|
|
vAscent = ascent;
|
|
} else {
|
|
if (vAscent == 0)
|
|
vAscent = ascent;
|
|
}
|
|
//}
|
|
|
|
child = child->GetNextBox();
|
|
}
|
|
|
|
return vAscent;
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::SetLargestSize(nsSize& aSize1, const nsSize& aSize2, PRBool aIsHorizontal)
|
|
{
|
|
if (aIsHorizontal)
|
|
{
|
|
if (aSize1.height < aSize2.height)
|
|
aSize1.height = aSize2.height;
|
|
} else {
|
|
if (aSize1.width < aSize2.width)
|
|
aSize1.width = aSize2.width;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::SetSmallestSize(nsSize& aSize1, const nsSize& aSize2, PRBool aIsHorizontal)
|
|
{
|
|
if (aIsHorizontal)
|
|
{
|
|
if (aSize1.height > aSize2.height)
|
|
aSize1.height = aSize2.height;
|
|
} else {
|
|
if (aSize1.width > aSize2.width)
|
|
aSize1.width = aSize2.width;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::AddLargestSize(nsSize& aSize, const nsSize& aSizeToAdd, PRBool aIsHorizontal)
|
|
{
|
|
if (aIsHorizontal)
|
|
AddCoord(aSize.width, aSizeToAdd.width);
|
|
else
|
|
AddCoord(aSize.height, aSizeToAdd.height);
|
|
|
|
SetLargestSize(aSize, aSizeToAdd, aIsHorizontal);
|
|
}
|
|
|
|
void
|
|
nsSprocketLayout::AddCoord(nscoord& aCoord, nscoord aCoordToAdd)
|
|
{
|
|
if (aCoord != NS_INTRINSICSIZE)
|
|
{
|
|
if (aCoordToAdd == NS_INTRINSICSIZE)
|
|
aCoord = aCoordToAdd;
|
|
else
|
|
aCoord += aCoordToAdd;
|
|
}
|
|
}
|
|
void
|
|
nsSprocketLayout::AddSmallestSize(nsSize& aSize, const nsSize& aSizeToAdd, PRBool aIsHorizontal)
|
|
{
|
|
if (aIsHorizontal)
|
|
AddCoord(aSize.width, aSizeToAdd.width);
|
|
else
|
|
AddCoord(aSize.height, aSizeToAdd.height);
|
|
|
|
SetSmallestSize(aSize, aSizeToAdd, aIsHorizontal);
|
|
}
|
|
|
|
PRBool
|
|
nsSprocketLayout::GetDefaultFlex(PRInt32& aFlex)
|
|
{
|
|
aFlex = 0;
|
|
return PR_TRUE;
|
|
}
|
|
|
|
void
|
|
nsBoxSize::Add(const nsSize& minSize,
|
|
const nsSize& prefSize,
|
|
const nsSize& maxSize,
|
|
nscoord aAscent,
|
|
nscoord aFlex,
|
|
PRBool aIsHorizontal)
|
|
{
|
|
nscoord pref2;
|
|
nscoord min2;
|
|
nscoord max2;
|
|
|
|
if (aIsHorizontal) {
|
|
pref2 = prefSize.width;
|
|
min2 = minSize.width;
|
|
max2 = maxSize.width;
|
|
} else {
|
|
pref2 = prefSize.height;
|
|
min2 = minSize.height;
|
|
max2 = maxSize.height;
|
|
}
|
|
|
|
if (min2 > min)
|
|
min = min2;
|
|
|
|
if (pref2 > pref)
|
|
pref = pref2;
|
|
|
|
if (max2 < max)
|
|
max = max2;
|
|
|
|
flex = aFlex;
|
|
|
|
if (!aIsHorizontal) {
|
|
if (aAscent > ascent)
|
|
ascent = aAscent;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsBoxSize::Add(const nsMargin& aMargin, PRBool aIsHorizontal)
|
|
{
|
|
if (aIsHorizontal) {
|
|
left += aMargin.left;
|
|
right += aMargin.right;
|
|
pref -= (aMargin.left + aMargin.right);
|
|
} else {
|
|
left += aMargin.top;
|
|
right += aMargin.bottom;
|
|
pref -= (aMargin.top + aMargin.bottom);
|
|
}
|
|
|
|
if (pref < min)
|
|
min = pref;
|
|
}
|
|
|
|
nsComputedBoxSize::nsComputedBoxSize()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void
|
|
nsComputedBoxSize::Clear()
|
|
{
|
|
resized = PR_FALSE;
|
|
valid = PR_FALSE;
|
|
size = 0;
|
|
next = nsnull;
|
|
}
|
|
|
|
nsBoxSize::nsBoxSize()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void
|
|
nsBoxSize::Clear()
|
|
{
|
|
pref = 0;
|
|
min = 0;
|
|
max = NS_INTRINSICSIZE;
|
|
collapsed = PR_FALSE;
|
|
ascent = 0;
|
|
left = 0;
|
|
right = 0;
|
|
flex = 0;
|
|
next = nsnull;
|
|
bogus = PR_FALSE;
|
|
}
|
|
|
|
|
|
void*
|
|
nsBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW
|
|
{
|
|
return aState.AllocateStackMemory(sz);
|
|
}
|
|
|
|
|
|
void
|
|
nsBoxSize::operator delete(void* aPtr, size_t sz)
|
|
{
|
|
}
|
|
|
|
|
|
void*
|
|
nsComputedBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW
|
|
{
|
|
return aState.AllocateStackMemory(sz);
|
|
}
|
|
|
|
void
|
|
nsComputedBoxSize::operator delete(void* aPtr, size_t sz)
|
|
{
|
|
}
|