summaryrefslogtreecommitdiffstats
path: root/layout/xul/nsStackLayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/xul/nsStackLayout.cpp')
-rw-r--r--layout/xul/nsStackLayout.cpp385
1 files changed, 385 insertions, 0 deletions
diff --git a/layout/xul/nsStackLayout.cpp b/layout/xul/nsStackLayout.cpp
new file mode 100644
index 000000000..6072e0612
--- /dev/null
+++ b/layout/xul/nsStackLayout.cpp
@@ -0,0 +1,385 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// Eric Vaughan
+// Netscape Communications
+//
+// See documentation in associated header file
+//
+
+#include "nsStackLayout.h"
+#include "nsCOMPtr.h"
+#include "nsBoxLayoutState.h"
+#include "nsBox.h"
+#include "nsBoxFrame.h"
+#include "nsGkAtoms.h"
+#include "nsIContent.h"
+#include "nsNameSpaceManager.h"
+
+using namespace mozilla;
+
+nsBoxLayout* nsStackLayout::gInstance = nullptr;
+
+#define SPECIFIED_LEFT (1 << NS_SIDE_LEFT)
+#define SPECIFIED_RIGHT (1 << NS_SIDE_RIGHT)
+#define SPECIFIED_TOP (1 << NS_SIDE_TOP)
+#define SPECIFIED_BOTTOM (1 << NS_SIDE_BOTTOM)
+
+nsresult
+NS_NewStackLayout(nsCOMPtr<nsBoxLayout>& aNewLayout)
+{
+ if (!nsStackLayout::gInstance) {
+ nsStackLayout::gInstance = new nsStackLayout();
+ NS_IF_ADDREF(nsStackLayout::gInstance);
+ }
+ // we have not instance variables so just return our static one.
+ aNewLayout = nsStackLayout::gInstance;
+ return NS_OK;
+}
+
+/*static*/ void
+nsStackLayout::Shutdown()
+{
+ NS_IF_RELEASE(gInstance);
+}
+
+nsStackLayout::nsStackLayout()
+{
+}
+
+/*
+ * Sizing: we are as wide as the widest child plus its left offset
+ * we are tall as the tallest child plus its top offset.
+ *
+ * Only children which have -moz-stack-sizing set to stretch-to-fit
+ * (the default) will be included in the size computations.
+ */
+
+nsSize
+nsStackLayout::GetXULPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState)
+{
+ nsSize prefSize (0, 0);
+
+ nsIFrame* child = nsBox::GetChildXULBox(aBox);
+ while (child) {
+ if (child->StyleXUL()->mStretchStack) {
+ nsSize pref = child->GetXULPrefSize(aState);
+
+ AddMargin(child, pref);
+ nsMargin offset;
+ GetOffset(child, offset);
+ pref.width += offset.LeftRight();
+ pref.height += offset.TopBottom();
+ AddLargestSize(prefSize, pref);
+ }
+
+ child = nsBox::GetNextXULBox(child);
+ }
+
+ AddBorderAndPadding(aBox, prefSize);
+
+ return prefSize;
+}
+
+nsSize
+nsStackLayout::GetXULMinSize(nsIFrame* aBox, nsBoxLayoutState& aState)
+{
+ nsSize minSize (0, 0);
+
+ nsIFrame* child = nsBox::GetChildXULBox(aBox);
+ while (child) {
+ if (child->StyleXUL()->mStretchStack) {
+ nsSize min = child->GetXULMinSize(aState);
+
+ AddMargin(child, min);
+ nsMargin offset;
+ GetOffset(child, offset);
+ min.width += offset.LeftRight();
+ min.height += offset.TopBottom();
+ AddLargestSize(minSize, min);
+ }
+
+ child = nsBox::GetNextXULBox(child);
+ }
+
+ AddBorderAndPadding(aBox, minSize);
+
+ return minSize;
+}
+
+nsSize
+nsStackLayout::GetXULMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState)
+{
+ nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
+
+ nsIFrame* child = nsBox::GetChildXULBox(aBox);
+ while (child) {
+ if (child->StyleXUL()->mStretchStack) {
+ nsSize min = child->GetXULMinSize(aState);
+ nsSize max = child->GetXULMaxSize(aState);
+
+ max = nsBox::BoundsCheckMinMax(min, max);
+
+ AddMargin(child, max);
+ nsMargin offset;
+ GetOffset(child, offset);
+ max.width += offset.LeftRight();
+ max.height += offset.TopBottom();
+ AddSmallestSize(maxSize, max);
+ }
+
+ child = nsBox::GetNextXULBox(child);
+ }
+
+ AddBorderAndPadding(aBox, maxSize);
+
+ return maxSize;
+}
+
+
+nscoord
+nsStackLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState)
+{
+ nscoord vAscent = 0;
+
+ nsIFrame* child = nsBox::GetChildXULBox(aBox);
+ while (child) {
+ nscoord ascent = child->GetXULBoxAscent(aState);
+ nsMargin margin;
+ child->GetXULMargin(margin);
+ ascent += margin.top;
+ if (ascent > vAscent)
+ vAscent = ascent;
+
+ child = nsBox::GetNextXULBox(child);
+ }
+
+ return vAscent;
+}
+
+uint8_t
+nsStackLayout::GetOffset(nsIFrame* aChild, nsMargin& aOffset)
+{
+ aOffset = nsMargin(0, 0, 0, 0);
+
+ // get the left, right, top and bottom offsets
+
+ // As an optimization, we cache the fact that we are not positioned to avoid
+ // wasting time fetching attributes.
+ if (aChild->IsXULBoxFrame() &&
+ (aChild->GetStateBits() & NS_STATE_STACK_NOT_POSITIONED))
+ return 0;
+
+ uint8_t offsetSpecified = 0;
+ nsIContent* content = aChild->GetContent();
+ if (content) {
+ bool ltr = aChild->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
+ nsAutoString value;
+ nsresult error;
+
+ content->GetAttr(kNameSpaceID_None, nsGkAtoms::start, value);
+ if (!value.IsEmpty()) {
+ value.Trim("%");
+ if (ltr) {
+ aOffset.left =
+ nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+ offsetSpecified |= SPECIFIED_LEFT;
+ } else {
+ aOffset.right =
+ nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+ offsetSpecified |= SPECIFIED_RIGHT;
+ }
+ }
+
+ content->GetAttr(kNameSpaceID_None, nsGkAtoms::end, value);
+ if (!value.IsEmpty()) {
+ value.Trim("%");
+ if (ltr) {
+ aOffset.right =
+ nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+ offsetSpecified |= SPECIFIED_RIGHT;
+ } else {
+ aOffset.left =
+ nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+ offsetSpecified |= SPECIFIED_LEFT;
+ }
+ }
+
+ content->GetAttr(kNameSpaceID_None, nsGkAtoms::left, value);
+ if (!value.IsEmpty()) {
+ value.Trim("%");
+ aOffset.left =
+ nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+ offsetSpecified |= SPECIFIED_LEFT;
+ }
+
+ content->GetAttr(kNameSpaceID_None, nsGkAtoms::right, value);
+ if (!value.IsEmpty()) {
+ value.Trim("%");
+ aOffset.right =
+ nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+ offsetSpecified |= SPECIFIED_RIGHT;
+ }
+
+ content->GetAttr(kNameSpaceID_None, nsGkAtoms::top, value);
+ if (!value.IsEmpty()) {
+ value.Trim("%");
+ aOffset.top =
+ nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+ offsetSpecified |= SPECIFIED_TOP;
+ }
+
+ content->GetAttr(kNameSpaceID_None, nsGkAtoms::bottom, value);
+ if (!value.IsEmpty()) {
+ value.Trim("%");
+ aOffset.bottom =
+ nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+ offsetSpecified |= SPECIFIED_BOTTOM;
+ }
+ }
+
+ if (!offsetSpecified && aChild->IsXULBoxFrame()) {
+ // If no offset was specified at all, then we cache this fact to avoid requerying
+ // CSS or the content model.
+ aChild->AddStateBits(NS_STATE_STACK_NOT_POSITIONED);
+ }
+
+ return offsetSpecified;
+}
+
+
+NS_IMETHODIMP
+nsStackLayout::XULLayout(nsIFrame* aBox, nsBoxLayoutState& aState)
+{
+ nsRect clientRect;
+ aBox->GetXULClientRect(clientRect);
+
+ bool grow;
+
+ do {
+ nsIFrame* child = nsBox::GetChildXULBox(aBox);
+ grow = false;
+
+ while (child)
+ {
+ nsMargin margin;
+ child->GetXULMargin(margin);
+ nsRect childRect(clientRect);
+ childRect.Deflate(margin);
+
+ if (childRect.width < 0)
+ childRect.width = 0;
+
+ if (childRect.height < 0)
+ childRect.height = 0;
+
+ nsRect oldRect(child->GetRect());
+ bool sizeChanged = !oldRect.IsEqualEdges(childRect);
+
+ // only lay out dirty children or children whose sizes have changed
+ if (sizeChanged || NS_SUBTREE_DIRTY(child)) {
+ // add in the child's margin
+ nsMargin margin;
+ child->GetXULMargin(margin);
+
+ // obtain our offset from the top left border of the stack's content box.
+ nsMargin offset;
+ uint8_t offsetSpecified = GetOffset(child, offset);
+
+ // Set the position and size based on which offsets have been specified:
+ // left only - offset from left edge, preferred width
+ // right only - offset from right edge, preferred width
+ // left and right - offset from left and right edges, width in between this
+ // neither - no offset, full width of stack
+ // Vertical direction is similar.
+ //
+ // Margins on the child are also included in the edge offsets
+ if (offsetSpecified) {
+ nsSize min = child->GetXULMinSize(aState);
+ nsSize max = child->GetXULMaxSize(aState);
+ if (offsetSpecified & SPECIFIED_LEFT) {
+ childRect.x = clientRect.x + offset.left + margin.left;
+ if (offsetSpecified & SPECIFIED_RIGHT) {
+ nscoord width = clientRect.width - offset.LeftRight() - margin.LeftRight();
+ childRect.width = clamped(width, min.width, max.width);
+ }
+ else {
+ nscoord width = child->GetXULPrefSize(aState).width;
+ childRect.width = clamped(width, min.width, max.width);
+ }
+ }
+ else if (offsetSpecified & SPECIFIED_RIGHT) {
+ nscoord width = child->GetXULPrefSize(aState).width;
+ childRect.width = clamped(width, min.width, max.width);
+ childRect.x = clientRect.XMost() - offset.right - margin.right - childRect.width;
+ }
+
+ if (offsetSpecified & SPECIFIED_TOP) {
+ childRect.y = clientRect.y + offset.top + margin.top;
+ if (offsetSpecified & SPECIFIED_BOTTOM) {
+ nscoord height = clientRect.height - offset.TopBottom() - margin.TopBottom();
+ childRect.height = clamped(height, min.height, max.height);
+ }
+ else {
+ nscoord height = child->GetXULPrefSize(aState).height;
+ childRect.height = clamped(height, min.height, max.height);
+ }
+ }
+ else if (offsetSpecified & SPECIFIED_BOTTOM) {
+ nscoord height = child->GetXULPrefSize(aState).height;
+ childRect.height = clamped(height, min.height, max.height);
+ childRect.y = clientRect.YMost() - offset.bottom - margin.bottom - childRect.height;
+ }
+ }
+
+ // Now place the child.
+ child->SetXULBounds(aState, childRect);
+
+ // Flow the child.
+ child->XULLayout(aState);
+
+ // Get the child's new rect.
+ childRect = child->GetRect();
+ childRect.Inflate(margin);
+
+ if (child->StyleXUL()->mStretchStack) {
+ // Did the child push back on us and get bigger?
+ if (offset.LeftRight() + childRect.width > clientRect.width) {
+ clientRect.width = childRect.width + offset.LeftRight();
+ grow = true;
+ }
+
+ if (offset.TopBottom() + childRect.height > clientRect.height) {
+ clientRect.height = childRect.height + offset.TopBottom();
+ grow = true;
+ }
+ }
+ }
+
+ child = nsBox::GetNextXULBox(child);
+ }
+ } while (grow);
+
+ // if some HTML inside us got bigger we need to force ourselves to
+ // get bigger
+ nsRect bounds(aBox->GetRect());
+ nsMargin bp;
+ aBox->GetXULBorderAndPadding(bp);
+ clientRect.Inflate(bp);
+
+ if (clientRect.width > bounds.width || clientRect.height > bounds.height)
+ {
+ if (clientRect.width > bounds.width)
+ bounds.width = clientRect.width;
+ if (clientRect.height > bounds.height)
+ bounds.height = clientRect.height;
+
+ aBox->SetXULBounds(aState, bounds);
+ }
+
+ return NS_OK;
+}
+