Work in progress on scroll frame code

This commit is contained in:
troy%netscape.com 1998-10-12 22:13:23 +00:00
parent 013f3adffa
commit 713db072d6
9 changed files with 421 additions and 403 deletions

View File

@ -325,6 +325,8 @@ protected:
nsIFrame* aParentFrame,
nsIFrame*& aFrame);
PRBool IsScrollable(nsIFrame* aFrame, const nsStyleDisplay* aDisplay);
protected:
PRUint32 mInHeap : 1;
PRUint32 mRefCnt : 31;
@ -1351,6 +1353,28 @@ HTMLStyleSheetImpl::ConstructFrameByDisplayType(nsIPresContext* aPresContext,
return rv;
}
PRBool
HTMLStyleSheetImpl::IsScrollable(nsIFrame* aFrame,
const nsStyleDisplay* aDisplay)
{
// If the overflow property is scroll then it's scrollable regardless
// of whether the content overflows the block.
// XXX This isn't correct. Only do this if the height is not allowed to
// grow to accomodate its child frames...
if (NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) {
return PR_TRUE;
}
#if 0
if ((NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) ||
// If the element has a fixed height (it isn't auto) and an overflow
// property of scroll or auto, then it's potentially scrollable.
// XXX Deal with width considerations, too
(NS_STYLE_OVERFLOW_AUTO == aDisplay->mOverflow)) {
#endif
return PR_FALSE;
}
NS_IMETHODIMP
HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext,
nsIContent* aContent,
@ -1406,20 +1430,32 @@ HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext,
}
#if 0
// If the frame is a block-level frame and it has a fixed height and overflow
// property of scroll, then wrap it in a scroll frame.
// XXX Deal with replaced elements and overflow of auto and width, too
nsIFrame* kidFrame = nsnull;
nsresult rv;
if ((NS_STYLE_OVERFLOW_SCROLL == kidDisplay->mOverflow) ||
(NS_STYLE_OVERFLOW_AUTO == kidDisplay->mOverflow)) {
rv = NS_NewScrollFrame(&kidFrame, aKid, aParentFrame);
if (NS_OK == rv) {
kidFrame->SetStyleContext(aPresContext, kidSC);
// If the frame is a block-level frame and is scrollable then wrap it
// in a scroll frame.
// XXX Applies to replaced elements, too, but how to tell if the element
// is replaced?
if (nsnull != aFrameSubTree) {
if (display->IsBlockLevel() && IsScrollable(aFrameSubTree, display)) {
nsIFrame* scrollFrame;
if NS_SUCCEEDED(NS_NewScrollFrame(aContent, aParentFrame, scrollFrame)) {
// The scroll frame gets the original style context, and the scrolled
// frame gets a pseudo style context.
// XXX Is this the best way to do this?
scrollFrame->SetStyleContext(aPresContext, styleContext);
nsIStyleContext* pseudoStyle;
pseudoStyle = aPresContext->ResolvePseudoStyleContextFor(nsHTMLAtoms::columnPseudo,
scrollFrame);
aFrameSubTree->SetStyleContext(aPresContext, pseudoStyle);
NS_RELEASE(pseudoStyle);
// Initialize the scroll frame
scrollFrame->Init(*aPresContext, aFrameSubTree);
aFrameSubTree = scrollFrame;
}
}
}
#endif
}
}
NS_RELEASE(styleContext);

View File

@ -35,7 +35,6 @@
#include "nsPlaceholderFrame.h"
#include "nsIHTMLContent.h"
#include "nsHTMLParts.h"
#include "nsScrollFrame.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsViewsCID.h"

View File

@ -303,6 +303,10 @@ nsresult
NS_NewWBRFrame(nsIContent* aContent, nsIFrame* aParentFrame,
nsIFrame*& aResult);
extern nsresult
NS_NewScrollFrame(nsIContent* aContent, nsIFrame* aParentFrame,
nsIFrame*& aResult);
// forms
extern nsresult
NS_NewFormFrame(nsIContent* aContent, nsIFrame* aParentFrame,

View File

@ -35,7 +35,6 @@
#include "nsPlaceholderFrame.h"
#include "nsIHTMLContent.h"
#include "nsHTMLParts.h"
#include "nsScrollFrame.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsViewsCID.h"

View File

@ -303,6 +303,10 @@ nsresult
NS_NewWBRFrame(nsIContent* aContent, nsIFrame* aParentFrame,
nsIFrame*& aResult);
extern nsresult
NS_NewScrollFrame(nsIContent* aContent, nsIFrame* aParentFrame,
nsIFrame*& aResult);
// forms
extern nsresult
NS_NewFormFrame(nsIContent* aContent, nsIFrame* aParentFrame,

View File

@ -15,7 +15,6 @@
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsScrollFrame.h"
#include "nsIPresContext.h"
#include "nsIStyleContext.h"
#include "nsIReflowCommand.h"
@ -27,264 +26,123 @@
#include "nsBodyFrame.h"
#include "nsHTMLContainerFrame.h"
#include "nsHTMLIIDs.h"
#include "nsCSSRendering.h"
#include "nsIScrollableView.h"
#include "nsWidgetsCID.h"
#include "nsBodyFrame.h"
static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID);
static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID);
class nsScrollBodyFrame : public nsContainerFrame {
//----------------------------------------------------------------------
class nsScrollViewFrame : public nsHTMLContainerFrame {
public:
nsScrollBodyFrame(nsIContent* aContent, nsIFrame* aParent);
nsScrollViewFrame(nsIContent* aContent, nsIFrame* aParent);
NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList);
NS_IMETHOD Reflow(nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
NS_IMETHOD ListTag(FILE* out = stdout) const;
protected:
void CreateFirstChild(nsIPresContext* aPresContext);
virtual PRIntn GetSkipSides() const;
};
nsScrollBodyFrame::nsScrollBodyFrame(nsIContent* aContent, nsIFrame* aParent)
: nsContainerFrame(aContent, aParent)
nsScrollViewFrame::nsScrollViewFrame(nsIContent* aContent, nsIFrame* aParent)
: nsHTMLContainerFrame(aContent, aParent)
{
}
void
nsScrollBodyFrame::CreateFirstChild(nsIPresContext* aPresContext)
{
// Are we paginated?
if (aPresContext->IsPaginated()) {
// Yes. Create the first page frame
mFirstChild = new nsPageFrame(mContent, this);/* XXX mContent won't work here */
} else {
if (nsnull != mContent) {
#if 0
// Create a frame for the child we are wrapping up
nsIContentDelegate* cd = mContent->GetDelegate(aPresContext);
if (nsnull != cd) {
nsIStyleContext* kidStyleContext =
aPresContext->ResolveStyleContextFor(mContent, this);
nsresult rv = cd->CreateFrame(aPresContext, mContent, this,
kidStyleContext, mFirstChild);
NS_RELEASE(kidStyleContext);
if (NS_OK == rv) {
mChildCount = 1;
mLastContentOffset = mFirstContentOffset;
}
NS_RELEASE(cd);
}
#else
nsBodyFrame::NewFrame(&mFirstChild, mContent, this);
#endif
}
}
}
// XXX Hack
#define PAGE_SPACING 100
NS_IMETHODIMP
nsScrollBodyFrame::Reflow(nsIPresContext& aPresContext,
nsScrollViewFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList)
{
mFirstChild = aChildList;
// XXX Unless it's already a body frame, child frames that are containers
// need to be wrapped in a body frame
return NS_OK;
}
NS_IMETHODIMP
nsScrollViewFrame::Reflow(nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
nsHTMLContainerFrame::CreateViewForFrame(aPresContext, this,
nsnull, PR_TRUE);
NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
("enter nsScrollBodyFrame::Reflow: maxSize=%d,%d",
("enter nsScrollViewFrame::Reflow: maxSize=%d,%d",
aReflowState.maxSize.width,
aReflowState.maxSize.height));
aStatus = NS_FRAME_COMPLETE;
// Create a view
if (eReflowReason_Initial == aReflowState.reason) {
// XXX It would be nice if we could do this sort of thing in our Init()
// member function instead of here...
nsHTMLContainerFrame::CreateViewForFrame(aPresContext, this,
nsnull, PR_TRUE);
}
// XXX Incremental reflow code doesn't handle page mode at all...
if (eReflowReason_Incremental == aReflowState.reason) {
// We don't expect the target of the reflow command to be the root
// content frame
#ifdef NS_DEBUG
nsIFrame* target;
aReflowState.reflowCommand->GetTarget(target);
NS_ASSERTION(target != this, "root content frame is reflow command target");
#endif
// Verify the next frame in the reflow chain is our child frame
nsIFrame* next;
aReflowState.reflowCommand->GetNext(next);
NS_ASSERTION(next == mFirstChild, "unexpected next reflow command frame");
// Scroll frame handles the border, and we handle the padding and background
const nsStyleSpacing* spacing = (const nsStyleSpacing*)
mStyleContext->GetStyleData(eStyleStruct_Spacing);
nsMargin padding;
spacing->CalcPaddingFor(this, padding);
nsSize maxSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE);
nsHTMLReflowState kidReflowState(aPresContext, next, aReflowState, maxSize);
nsIHTMLReflow* htmlReflow;
if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) {
// Dispatch the reflow command to our child frame. Allow it to be as high
// as it wants
ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus);
// Place and size the child. Make sure the child is at least as
// tall as our max size (the containing window)
if (aDesiredSize.height < aReflowState.maxSize.height) {
aDesiredSize.height = aReflowState.maxSize.height;
}
nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height);
mFirstChild->SetRect(rect);
// XXX Why are we sending a DidReflow() notification here?
htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
}
// Allow the child frame to be as wide as our max width (minus scrollbar
// width and padding), and as high as it wants to be.
nsSize maxSize;
nsIDeviceContext* dc = aPresContext.GetDeviceContext();
float sbWidth, sbHeight;
} else {
// Do we have any children?
if (nsnull == mFirstChild) {
// No, create the first child frame
CreateFirstChild(&aPresContext);
}
dc->GetScrollBarDimensions(sbWidth, sbHeight);
maxSize.width = aReflowState.maxSize.width - NSToCoordRound(sbWidth) -
(padding.left + padding.right);
NS_RELEASE(dc);
maxSize.height = NS_UNCONSTRAINEDSIZE;
// Resize our frames
if (nsnull != mFirstChild) {
if (aPresContext.IsPaginated()) {
nscoord y = PAGE_SPACING;
nsHTMLReflowMetrics kidSize(aDesiredSize.maxElementSize);
nsSize pageSize(aPresContext.GetPageWidth(),
aPresContext.GetPageHeight());
// Tile the pages vertically
for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) {
// Reflow the page
nsHTMLReflowState kidReflowState(aPresContext, kidFrame, aReflowState, pageSize);
nsReflowStatus status;
// Reflow the child
nsHTMLReflowMetrics kidMetrics(aDesiredSize.maxElementSize);
nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, maxSize);
// Place and size the page. If the page is narrower than our
// max width then center it horizontally
nsIDeviceContext *dx = aPresContext.GetDeviceContext();
float sbWidth, sbHeight;
dx->GetScrollBarDimensions(sbWidth, sbHeight);
nscoord extra = aReflowState.maxSize.width - kidSize.width -
NSToCoordRound(sbWidth);
NS_RELEASE(dx);
nscoord x = extra > 0 ? extra / 2 : 0;
nsIHTMLReflow* htmlReflow;
ReflowChild(mFirstChild, aPresContext, kidMetrics, kidReflowState, aStatus);
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
// Place and size the child
nsRect rect(padding.left, padding.top, kidMetrics.width, kidMetrics.height);
mFirstChild->SetRect(rect);
if (NS_OK == kidFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) {
htmlReflow->WillReflow(aPresContext);
ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState, status);
kidFrame->SetRect(nsRect(x, y, kidSize.width, kidSize.height));
htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
y += kidSize.height;
// Leave a slight gap between the pages
y += PAGE_SPACING;
// Is the page complete?
nsIFrame* kidNextInFlow;
kidFrame->GetNextInFlow(kidNextInFlow);
if (NS_FRAME_IS_COMPLETE(status)) {
NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
} else if (nsnull == kidNextInFlow) {
// The page isn't complete and it doesn't have a next-in-flow so
// create a continuing page
nsIStyleContext* kidSC;
kidFrame->GetStyleContext(&aPresContext, kidSC);
nsIFrame* continuingPage;
nsresult rv = kidFrame->CreateContinuingFrame(aPresContext, this,
kidSC, continuingPage);
NS_RELEASE(kidSC);
// Add it to our child list
#ifdef NS_DEBUG
nsIFrame* kidNextSibling;
kidFrame->GetNextSibling(kidNextSibling);
NS_ASSERTION(nsnull == kidNextSibling, "unexpected sibling");
#endif
kidFrame->SetNextSibling(continuingPage);
}
}
// Get the next page
kidFrame->GetNextSibling(kidFrame);
}
// Return our desired size
aDesiredSize.width = aReflowState.maxSize.width;
aDesiredSize.height = y;
} else {
// Allow the frame to be as wide as our max width, and as high
// as it wants to be.
nsSize maxSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE);
nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, maxSize);
nsIHTMLReflow* htmlReflow;
if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) {
// Get the child's desired size. Our child's desired height is our
// desired size
htmlReflow->WillReflow(aPresContext);
ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus);
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
#if 0
// Place and size the child. Make sure the child is at least as
// tall as our max size (the containing window)
if (aDesiredSize.height < aReflowState.maxSize.height) {
aDesiredSize.height = aReflowState.maxSize.height;
}
#endif
// Place and size the child
nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height);
mFirstChild->SetRect(rect);
htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
}
}
}
else {
aDesiredSize.width = aReflowState.maxSize.width;
aDesiredSize.height = 1;
aDesiredSize.ascent = aDesiredSize.height;
aDesiredSize.descent = 0;
if (nsnull != aDesiredSize.maxElementSize) {
aDesiredSize.maxElementSize->width = 0;
aDesiredSize.maxElementSize->height = 0;
}
}
// Determine our size. Our width is our maxWidth and our height is the max
// of our child's height plus padding and our maxHeight (if our maxHeight is
// constrained).
aDesiredSize.width = aReflowState.maxSize.width;
if (NS_UNCONSTRAINEDSIZE == aReflowState.maxSize.height) {
aDesiredSize.height = kidMetrics.height + padding.top + padding.bottom;
}
else {
aDesiredSize.height = aReflowState.maxSize.height;
}
NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
("exit nsScrollBodyFrame::Reflow: status=%d width=%d height=%d",
aStatus, aDesiredSize.width, aDesiredSize.height));
("exit nsScrollViewFrame::Reflow: status=%d width=%d height=%d",
aStatus, aDesiredSize.width, aDesiredSize.height));
return NS_OK;
}
NS_IMETHODIMP
nsScrollBodyFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
PRIntn
nsScrollViewFrame::GetSkipSides() const
{
// If we're paginated then fill the dirty rect with white
if (aPresContext.IsPaginated()) {
// Cross hatching would be nicer...
aRenderingContext.SetColor(NS_RGB(255,255,255));
aRenderingContext.FillRect(aDirtyRect);
}
nsContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect);
return NS_OK;
// Scroll frame handles the border...
return 0xF;
}
NS_IMETHODIMP
nsScrollBodyFrame::ListTag(FILE* out) const
nsScrollViewFrame::ListTag(FILE* out) const
{
fputs("*scrollbodyframe<", out);
fputs("*scrollviewframe<", out);
nsIAtom* atom;
mContent->GetTag(atom);
if (nsnull != atom) {
@ -299,36 +157,70 @@ nsScrollBodyFrame::ListTag(FILE* out) const
//----------------------------------------------------------------------
class nsScrollInnerFrame : public nsContainerFrame {
/**
* The scrolling view frame creates and manages the scrolling view.
* It creates a nsScrollViewFrame which handles padding and rendering
* of the background.
*/
class nsScrollingViewFrame : public nsHTMLContainerFrame {
public:
nsScrollInnerFrame(nsIContent* aContent, nsIFrame* aParent);
nsScrollingViewFrame(nsIContent* aContent, nsIFrame* aParent);
NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList);
NS_IMETHOD Reflow(nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
NS_IMETHOD ListTag(FILE* out = stdout) const;
protected:
virtual PRIntn GetSkipSides() const;
};
nsScrollInnerFrame::nsScrollInnerFrame(nsIContent* aContent, nsIFrame* aParent)
: nsContainerFrame(aContent, aParent)
nsScrollingViewFrame::nsScrollingViewFrame(nsIContent* aContent, nsIFrame* aParent)
: nsHTMLContainerFrame(aContent, aParent)
{
}
NS_IMETHODIMP
nsScrollInnerFrame::Reflow(nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
nsScrollingViewFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList)
{
// Create a scroll view frame
mFirstChild = new nsScrollViewFrame(mContent, this);
if (nsnull == mFirstChild) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Have it use our style context
mFirstChild->SetStyleContext(&aPresContext, mStyleContext);
// Reset the child frame's geometric and content parent to be
// the scroll view frame
aChildList->SetGeometricParent(mFirstChild);
aChildList->SetContentParent(mFirstChild);
// Init the scroll view frame passing it the child list
return mFirstChild->Init(aPresContext, aChildList);
}
//XXX incremental reflow pass through
NS_IMETHODIMP
nsScrollingViewFrame::Reflow(nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
("enter nsScrollInnerFrame::Reflow: maxSize=%d,%d",
("enter nsScrollingViewFrame::Reflow: maxSize=%d,%d",
aReflowState.maxSize.width,
aReflowState.maxSize.height));
// Make sure we have a scrolling view
nsIView* view;
GetView(view);
if (nsnull == view) {
@ -350,77 +242,75 @@ nsScrollInnerFrame::Reflow(nsIPresContext& aPresContext,
nsnull,
kIViewIID,
(void **)&view);
// XXX We want the scrolling view to have a widget to clip any child
// widgets that aren't visible, e.g. form elements, but there's currently
// a bug which is why it's commented out
if ((NS_OK != rv) || (NS_OK != view->Init(viewManager,
mRect,
parentView))) {
parentView,
nsnull))) {
// &kWidgetCID))) {
NS_RELEASE(viewManager);
return rv;
}
// Insert new view as a child of the parent view
viewManager->InsertChild(parentView, view, 0);
NS_RELEASE(viewManager);
// NS_RELEASE(parentView);
SetView(view);
NS_RELEASE(viewManager);
}
if (nsnull == view) {
return NS_OK;
}
// NS_RELEASE(view);
if (nsnull == mFirstChild) {
mFirstChild = new nsScrollBodyFrame(mContent, this);
}
// Reflow the child and get its desired size. Let the child's height be
// whatever it wants
nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState,
nsSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE));
// Allow the child frame to be as wide as our max width (minus a
// scroll bar width), and as high as it wants to be.
nsSize maxSize;
nsIDeviceContext* dc = aPresContext.GetDeviceContext();
float sbWidth, sbHeight;
dc->GetScrollBarDimensions(sbWidth, sbHeight);
maxSize.width = aReflowState.maxSize.width - NSToCoordRound(sbWidth);
NS_RELEASE(dc);
maxSize.height = NS_UNCONSTRAINEDSIZE;
ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus);
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
// Reflow the child
nsHTMLReflowMetrics kidMetrics(aDesiredSize.maxElementSize);
// Place and size the child
nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height);
mFirstChild->SetRect(rect);
nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, maxSize);
nsIHTMLReflow* htmlReflow;
if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) {
htmlReflow->WillReflow(aPresContext);
ReflowChild(mFirstChild, aPresContext, kidMetrics, kidReflowState, aStatus);
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
// Place and size the child
nsRect rect(0, 0, kidMetrics.width, kidMetrics.height);
mFirstChild->SetRect(rect);
htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
// The scroll view frame either shrink wraps around it's single
// child OR uses the style width/height.
if (aReflowState.HaveConstrainedWidth()) {
aDesiredSize.width = aReflowState.minWidth;
}
// Determine our size. Our width is our maxWidth and our height is
// either our child's height or our maxHeight if our maxHeight is
// constrained.
aDesiredSize.width = aReflowState.maxSize.width;
if (NS_UNCONSTRAINEDSIZE == aReflowState.maxSize.height) {
aDesiredSize.height = kidMetrics.height;
}
else {
aDesiredSize.height = aReflowState.maxSize.height;
if (aReflowState.HaveConstrainedHeight()) {
aDesiredSize.height = aReflowState.minHeight;
}
aDesiredSize.ascent = aDesiredSize.height;
aDesiredSize.descent = 0;
NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
("exit nsScrollInnerFrame::Reflow: status=%d width=%d height=%d",
("exit nsScrollingViewFrame::Reflow: status=%d width=%d height=%d",
aStatus, aDesiredSize.width, aDesiredSize.height));
return NS_OK;
}
NS_IMETHODIMP
nsScrollInnerFrame::ListTag(FILE* out) const
nsScrollingViewFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
fputs("*scrollinnerframe<", out);
// Paint our children
return nsContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect);
}
PRIntn
nsScrollingViewFrame::GetSkipSides() const
{
return 0;
}
NS_IMETHODIMP
nsScrollingViewFrame::ListTag(FILE* out) const
{
fputs("*scrollingviewframe<", out);
nsIAtom* atom;
mContent->GetTag(atom);
if (nsnull != atom) {
@ -435,108 +325,160 @@ nsScrollInnerFrame::ListTag(FILE* out) const
//----------------------------------------------------------------------
class nsScrollOuterFrame : public nsHTMLContainerFrame {
/**
* The scroll frame basically acts as a border frame. It leaves room for and
* renders the border. It creates a nsScrollingViewFrame which creates and
* manages the scrollable view.
*/
class nsScrollFrame : public nsHTMLContainerFrame {
public:
nsScrollOuterFrame(nsIContent* aContent, nsIFrame* aParent);
nsScrollFrame(nsIContent* aContent, nsIFrame* aParent);
NS_IMETHOD Init(nsIPresContext& aPresContext, nsIFrame* aChildList);
NS_IMETHOD Reflow(nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
NS_IMETHOD ListTag(FILE* out = stdout) const;
protected:
virtual PRIntn GetSkipSides() const;
};
nsScrollOuterFrame::nsScrollOuterFrame(nsIContent* aContent, nsIFrame* aParent)
nsScrollFrame::nsScrollFrame(nsIContent* aContent, nsIFrame* aParent)
: nsHTMLContainerFrame(aContent, aParent)
{
}
NS_IMETHODIMP
nsScrollFrame::Init(nsIPresContext& aPresContext, nsIFrame* aChildList)
{
NS_PRECONDITION(nsnull != aChildList, "no child frame");
// Create a scrolling view frame
mFirstChild = new nsScrollingViewFrame(mContent, this);
if (nsnull == mFirstChild) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Have it use our style context
mFirstChild->SetStyleContext(&aPresContext, mStyleContext);
// Reset the child frame's geometric and content parent to be
// the scrolling view frame
#ifdef NS_DEBUG
// Verify that there's only one child frame
nsIFrame* nextSibling;
aChildList->GetNextSibling(nextSibling);
NS_ASSERTION(nsnull == nextSibling, "expected only one child");
#endif
aChildList->SetGeometricParent(mFirstChild);
aChildList->SetContentParent(mFirstChild);
// Init the scrollable view frame passing it the child list
return mFirstChild->Init(aPresContext, aChildList);
}
//XXX incremental reflow pass through
NS_IMETHODIMP
nsScrollOuterFrame::Reflow(nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
nsScrollFrame::Reflow(nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
("enter nsScrollOuterFrame::Reflow: maxSize=%d,%d",
("enter nsScrollFrame::Reflow: maxSize=%d,%d",
aReflowState.maxSize.width,
aReflowState.maxSize.height));
if (nsnull == mFirstChild) {
mFirstChild = new nsScrollInnerFrame(mContent, this);
}
// We handle the border only
const nsStyleSpacing* spacing = (const nsStyleSpacing*)
mStyleContext->GetStyleData(eStyleStruct_Spacing);
nsMargin borderPadding;
spacing->CalcBorderPaddingFor(this, borderPadding);
nscoord lr = borderPadding.left + borderPadding.right;
nscoord tb = borderPadding.top + borderPadding.bottom;
nsMargin border;
spacing->CalcBorderFor(this, border);
nscoord lr = border.left + border.right;
nscoord tb = border.top + border.bottom;
// Get style size and determine how much area is available for the
// child (the scroll inner frame) to layout into.
nsSize maxSize;
// Compute the scrolling view frame's max size taking into account our
// borders
nsSize kidMaxSize;
if (aReflowState.HaveConstrainedWidth()) {
maxSize.width = aReflowState.minWidth - lr;
// This value reflects space for the content area only, so don't
// subtract for borders...
kidMaxSize.width = aReflowState.minWidth;
}
else {
maxSize.width = aReflowState.maxSize.width;
kidMaxSize.width = aReflowState.maxSize.width;
if (NS_UNCONSTRAINEDSIZE != kidMaxSize.width) {
kidMaxSize.width -= lr;
}
}
if (aReflowState.HaveConstrainedHeight()) {
maxSize.height = aReflowState.minHeight - tb;
// This value reflects space for the content area only, so don't
// subtract for borders...
kidMaxSize.height = aReflowState.minHeight;
}
else {
maxSize.height = NS_UNCONSTRAINEDSIZE;
kidMaxSize.height = NS_UNCONSTRAINEDSIZE;
if (NS_UNCONSTRAINEDSIZE != kidMaxSize.height) {
kidMaxSize.height -= tb;
}
}
// Reflow the child and get its desired size
nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, maxSize);
nsIHTMLReflow* htmlReflow;
nsHTMLReflowState kidReflowState(aPresContext, mFirstChild, aReflowState, kidMaxSize);
if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) {
htmlReflow->WillReflow(aPresContext);
ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus);
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
ReflowChild(mFirstChild, aPresContext, aDesiredSize, kidReflowState, aStatus);
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
// Place and size the child
nsRect rect(borderPadding.left, borderPadding.top,
aDesiredSize.width, aDesiredSize.height);
mFirstChild->SetRect(rect);
htmlReflow->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
}
// Place and size the child
nsRect rect(border.left, border.top, aDesiredSize.width, aDesiredSize.height);
mFirstChild->SetRect(rect);
// The scroll outer frame either shrink wraps around it's single
// child OR uses the style width/height.
if (aReflowState.HaveConstrainedWidth()) {
aDesiredSize.width = aReflowState.minWidth;
}
else {
aDesiredSize.width += lr;
}
if (aReflowState.HaveConstrainedHeight()) {
aDesiredSize.height = aReflowState.minHeight;
}
else {
aDesiredSize.height += tb;
}
// Compute our desired size by adding in space for the borders
aDesiredSize.width += lr;
aDesiredSize.height += tb;
aDesiredSize.ascent = aDesiredSize.height;
aDesiredSize.descent = 0;
NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
("exit nsScrollOuterFrame::Reflow: status=%d width=%d height=%d",
("exit nsScrollFrame::Reflow: status=%d width=%d height=%d",
aStatus, aDesiredSize.width, aDesiredSize.height));
return NS_OK;
}
NS_IMETHODIMP
nsScrollOuterFrame::ListTag(FILE* out) const
nsScrollFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
fputs("*scrollouterframe<", out);
// Paint our border only (no background)
const nsStyleSpacing* spacing =
(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
nsRect rect(0, 0, mRect.width, mRect.height);
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
aDirtyRect, rect, *spacing, 0);
// Paint our children
return nsContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect);
}
PRIntn
nsScrollFrame::GetSkipSides() const
{
return 0;
}
NS_IMETHODIMP
nsScrollFrame::ListTag(FILE* out) const
{
fputs("*scrollframe<", out);
nsIAtom* atom;
mContent->GetTag(atom);
if (nsnull != atom) {
@ -549,27 +491,16 @@ nsScrollOuterFrame::ListTag(FILE* out) const
return NS_OK;
}
PRIntn
nsScrollOuterFrame::GetSkipSides() const
{
return 0;
}
//----------------------------------------------------------------------
nsresult
NS_NewScrollFrame(nsIFrame** aInstancePtrResult,
nsIContent* aContent,
nsIFrame* aParent)
NS_NewScrollFrame(nsIContent* aContent,
nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
nsIFrame* it = new nsScrollOuterFrame(aContent, aParent);
if (nsnull == it) {
aResult = new nsScrollFrame(aContent, aParentFrame);
if (nsnull == aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aInstancePtrResult = it;
return NS_OK;
}

View File

@ -1,27 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef nsScrollFrame_h___
#define nsScrollFrame_h___
#include "nsHTMLContainerFrame.h"
extern nsresult NS_NewScrollFrame(nsIFrame** aInstancePtrResult,
nsIContent* aContent,
nsIFrame* aParent);
#endif /* nsScrollFrame_h___ */

View File

@ -325,6 +325,8 @@ protected:
nsIFrame* aParentFrame,
nsIFrame*& aFrame);
PRBool IsScrollable(nsIFrame* aFrame, const nsStyleDisplay* aDisplay);
protected:
PRUint32 mInHeap : 1;
PRUint32 mRefCnt : 31;
@ -1351,6 +1353,28 @@ HTMLStyleSheetImpl::ConstructFrameByDisplayType(nsIPresContext* aPresContext,
return rv;
}
PRBool
HTMLStyleSheetImpl::IsScrollable(nsIFrame* aFrame,
const nsStyleDisplay* aDisplay)
{
// If the overflow property is scroll then it's scrollable regardless
// of whether the content overflows the block.
// XXX This isn't correct. Only do this if the height is not allowed to
// grow to accomodate its child frames...
if (NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) {
return PR_TRUE;
}
#if 0
if ((NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) ||
// If the element has a fixed height (it isn't auto) and an overflow
// property of scroll or auto, then it's potentially scrollable.
// XXX Deal with width considerations, too
(NS_STYLE_OVERFLOW_AUTO == aDisplay->mOverflow)) {
#endif
return PR_FALSE;
}
NS_IMETHODIMP
HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext,
nsIContent* aContent,
@ -1406,20 +1430,32 @@ HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext,
}
#if 0
// If the frame is a block-level frame and it has a fixed height and overflow
// property of scroll, then wrap it in a scroll frame.
// XXX Deal with replaced elements and overflow of auto and width, too
nsIFrame* kidFrame = nsnull;
nsresult rv;
if ((NS_STYLE_OVERFLOW_SCROLL == kidDisplay->mOverflow) ||
(NS_STYLE_OVERFLOW_AUTO == kidDisplay->mOverflow)) {
rv = NS_NewScrollFrame(&kidFrame, aKid, aParentFrame);
if (NS_OK == rv) {
kidFrame->SetStyleContext(aPresContext, kidSC);
// If the frame is a block-level frame and is scrollable then wrap it
// in a scroll frame.
// XXX Applies to replaced elements, too, but how to tell if the element
// is replaced?
if (nsnull != aFrameSubTree) {
if (display->IsBlockLevel() && IsScrollable(aFrameSubTree, display)) {
nsIFrame* scrollFrame;
if NS_SUCCEEDED(NS_NewScrollFrame(aContent, aParentFrame, scrollFrame)) {
// The scroll frame gets the original style context, and the scrolled
// frame gets a pseudo style context.
// XXX Is this the best way to do this?
scrollFrame->SetStyleContext(aPresContext, styleContext);
nsIStyleContext* pseudoStyle;
pseudoStyle = aPresContext->ResolvePseudoStyleContextFor(nsHTMLAtoms::columnPseudo,
scrollFrame);
aFrameSubTree->SetStyleContext(aPresContext, pseudoStyle);
NS_RELEASE(pseudoStyle);
// Initialize the scroll frame
scrollFrame->Init(*aPresContext, aFrameSubTree);
aFrameSubTree = scrollFrame;
}
}
}
#endif
}
}
NS_RELEASE(styleContext);

View File

@ -325,6 +325,8 @@ protected:
nsIFrame* aParentFrame,
nsIFrame*& aFrame);
PRBool IsScrollable(nsIFrame* aFrame, const nsStyleDisplay* aDisplay);
protected:
PRUint32 mInHeap : 1;
PRUint32 mRefCnt : 31;
@ -1351,6 +1353,28 @@ HTMLStyleSheetImpl::ConstructFrameByDisplayType(nsIPresContext* aPresContext,
return rv;
}
PRBool
HTMLStyleSheetImpl::IsScrollable(nsIFrame* aFrame,
const nsStyleDisplay* aDisplay)
{
// If the overflow property is scroll then it's scrollable regardless
// of whether the content overflows the block.
// XXX This isn't correct. Only do this if the height is not allowed to
// grow to accomodate its child frames...
if (NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) {
return PR_TRUE;
}
#if 0
if ((NS_STYLE_OVERFLOW_SCROLL == aDisplay->mOverflow) ||
// If the element has a fixed height (it isn't auto) and an overflow
// property of scroll or auto, then it's potentially scrollable.
// XXX Deal with width considerations, too
(NS_STYLE_OVERFLOW_AUTO == aDisplay->mOverflow)) {
#endif
return PR_FALSE;
}
NS_IMETHODIMP
HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext,
nsIContent* aContent,
@ -1406,20 +1430,32 @@ HTMLStyleSheetImpl::ConstructFrame(nsIPresContext* aPresContext,
}
#if 0
// If the frame is a block-level frame and it has a fixed height and overflow
// property of scroll, then wrap it in a scroll frame.
// XXX Deal with replaced elements and overflow of auto and width, too
nsIFrame* kidFrame = nsnull;
nsresult rv;
if ((NS_STYLE_OVERFLOW_SCROLL == kidDisplay->mOverflow) ||
(NS_STYLE_OVERFLOW_AUTO == kidDisplay->mOverflow)) {
rv = NS_NewScrollFrame(&kidFrame, aKid, aParentFrame);
if (NS_OK == rv) {
kidFrame->SetStyleContext(aPresContext, kidSC);
// If the frame is a block-level frame and is scrollable then wrap it
// in a scroll frame.
// XXX Applies to replaced elements, too, but how to tell if the element
// is replaced?
if (nsnull != aFrameSubTree) {
if (display->IsBlockLevel() && IsScrollable(aFrameSubTree, display)) {
nsIFrame* scrollFrame;
if NS_SUCCEEDED(NS_NewScrollFrame(aContent, aParentFrame, scrollFrame)) {
// The scroll frame gets the original style context, and the scrolled
// frame gets a pseudo style context.
// XXX Is this the best way to do this?
scrollFrame->SetStyleContext(aPresContext, styleContext);
nsIStyleContext* pseudoStyle;
pseudoStyle = aPresContext->ResolvePseudoStyleContextFor(nsHTMLAtoms::columnPseudo,
scrollFrame);
aFrameSubTree->SetStyleContext(aPresContext, pseudoStyle);
NS_RELEASE(pseudoStyle);
// Initialize the scroll frame
scrollFrame->Init(*aPresContext, aFrameSubTree);
aFrameSubTree = scrollFrame;
}
}
}
#endif
}
}
NS_RELEASE(styleContext);