WillReflow/DidReflow changes and changes to the way view positioning

and sizing works
This commit is contained in:
troy@netscape.com
1999-11-19 15:33:29 +00:00
parent 823a12d10c
commit 3b67f2f17c
84 changed files with 2020 additions and 783 deletions

View File

@@ -39,6 +39,7 @@
#include "nsIPresShell.h"
#include "nsCOMPtr.h"
#include "nsLayoutAtoms.h"
#include "nsIViewManager.h"
#ifdef NS_DEBUG
#undef NOISY
@@ -107,6 +108,8 @@ nsContainerFrame::DidReflow(nsIPresContext& aPresContext,
// the NS_FRAME_IN_REFLOW bit
nsresult result = nsFrame::DidReflow(aPresContext, aStatus);
// XXX TROY
#if 0
if (NS_FRAME_REFLOW_FINISHED == aStatus) {
// Apply DidReflow to each and every list that this frame implements
nsIAtom* listName = nsnull;
@@ -122,6 +125,7 @@ nsContainerFrame::DidReflow(nsIPresContext& aPresContext,
GetAdditionalChildListName(listIndex++, &listName);
} while(nsnull != listName);
}
#endif
NS_FRAME_TRACE_OUT("nsContainerFrame::DidReflow");
return result;
@@ -372,17 +376,194 @@ nsContainerFrame::ReplaceFrame(nsIPresContext& aPresContext,
/////////////////////////////////////////////////////////////////////////////
// Helper member functions
void
nsContainerFrame::PositionFrameView(nsIPresContext* aPresContext,
nsIFrame* aKidFrame,
nsIView* aView)
{
if (aView) {
// Position view relative to its parent, not relative to aKidFrame's
// frame which may not have a view
nsIView* containingView;
nsPoint origin;
nsIView* parentView;
aView->GetParent(parentView);
aKidFrame->GetOffsetFromView(aPresContext, origin, &containingView);
if (containingView == parentView) {
nsIViewManager *vm;
aView->GetViewManager(vm);
vm->MoveViewTo(aView, origin.x, origin.y);
NS_RELEASE(vm);
}
}
}
void
nsContainerFrame::SyncFrameViewAfterReflow(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIView* aView,
nsRect* aCombinedArea,
PRUint32 aFlags)
{
if (aView) {
nsIViewManager *vm;
nsFrameState kidState;
nsSize frameSize;
aView->GetViewManager(vm);
aFrame->GetFrameState(&kidState);
aFrame->GetSize(frameSize);
// Make sure the view is sized and positioned correctly
if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
nsIView* containingView;
nsPoint origin;
nsIView* parentView;
aView->GetParent(parentView);
aFrame->GetOffsetFromView(aPresContext, origin, &containingView);
if (containingView == parentView) {
vm->MoveViewTo(aView, origin.x, origin.y);
}
}
if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) {
// If the frame has child frames that stick outside the content
// area, then size the view large enough to include those child
// frames
if ((kidState & NS_FRAME_OUTSIDE_CHILDREN) && aCombinedArea) {
vm->ResizeView(aView, aCombinedArea->XMost(), aCombinedArea->YMost());
} else {
vm->ResizeView(aView, frameSize.width, frameSize.height);
}
}
const nsStyleColor* color;
const nsStyleDisplay* display;
aFrame->GetStyleData(eStyleStruct_Color, (const nsStyleStruct*&)color);
aFrame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)display);
// Set the view's opacity
vm->SetViewOpacity(aView, color->mOpacity);
// See if the view should be hidden or visible
PRBool viewIsVisible = PR_TRUE;
PRBool viewHasTransparentContent = (color->mBackgroundFlags &
NS_STYLE_BG_COLOR_TRANSPARENT) == NS_STYLE_BG_COLOR_TRANSPARENT;
if (NS_STYLE_VISIBILITY_COLLAPSE == display->mVisible) {
viewIsVisible = PR_FALSE;
}
else if (NS_STYLE_VISIBILITY_HIDDEN == display->mVisible) {
// If it has a widget, hide the view because the widget can't deal with it
nsIWidget* widget = nsnull;
aView->GetWidget(widget);
if (widget) {
viewIsVisible = PR_FALSE;
NS_RELEASE(widget);
}
else {
// If it's a scroll frame, then hide the view. This means that
// child elements can't override their parent's visibility, but
// it's not practical to leave it visible in all cases because
// the scrollbars will be showing
nsIAtom* frameType;
aFrame->GetFrameType(&frameType);
if (frameType == nsLayoutAtoms::scrollFrame) {
viewIsVisible = PR_FALSE;
} else {
// If we're a container element, then leave the view visible, but
// mark it as having transparent content. The reason we need to
// do this is that child elements can override their parent's
// hidden visibility and be visible anyway
nsIFrame* firstChild;
aFrame->FirstChild(nsnull, &firstChild);
if (firstChild) {
// Not a left frame, so the view needs to be visible, but marked
// as having transparent content
viewHasTransparentContent = PR_TRUE;
} else {
// Leaf frame so go ahead and hide the view
viewIsVisible = PR_FALSE;
}
}
NS_IF_RELEASE(frameType);
}
}
// If the frame has visible content that overflows the content area, then we
// need the view marked as having transparent content
if (NS_STYLE_OVERFLOW_VISIBLE == display->mOverflow) {
if (kidState & NS_FRAME_OUTSIDE_CHILDREN) {
viewHasTransparentContent = PR_TRUE;
}
}
// Make sure visibility is correct
vm->SetViewVisibility(aView, viewIsVisible ? nsViewVisibility_kShow :
nsViewVisibility_kHide);
// Make sure content transparency is correct
if (viewIsVisible) {
vm->SetViewContentTransparency(aView, viewHasTransparentContent);
}
// Clip applies to block-level and replaced elements with overflow
// set to other than 'visible'
if (display->IsBlockLevel()) {
if (display->mOverflow == NS_STYLE_OVERFLOW_HIDDEN) {
nscoord left, top, right, bottom;
// Start with the 'auto' values and then factor in user
// specified values
left = top = 0;
right = frameSize.width;
bottom = frameSize.height;
if (0 == (NS_STYLE_CLIP_TOP_AUTO & display->mClipFlags)) {
top += display->mClip.top;
}
if (0 == (NS_STYLE_CLIP_RIGHT_AUTO & display->mClipFlags)) {
right -= display->mClip.right;
}
if (0 == (NS_STYLE_CLIP_BOTTOM_AUTO & display->mClipFlags)) {
bottom -= display->mClip.bottom;
}
if (0 == (NS_STYLE_CLIP_LEFT_AUTO & display->mClipFlags)) {
left += display->mClip.left;
}
aView->SetClip(left, top, right, bottom);
} else {
// Make sure no clip is set
aView->SetClip(0, 0, 0, 0);
}
}
NS_RELEASE(vm);
}
}
/**
* Queries the child frame for the nsIHTMLReflow interface and if it's
* supported invokes the WillReflow() and Reflow() member functions. If
* the reflow succeeds and the child frame is complete, deletes any
* next-in-flows using DeleteChildsNextInFlow()
* Invokes the WillReflow() function, positions the frame and its view (if
* requested), and then calls Reflow(). If the reflow succeeds and the child
* frame is complete, deletes any next-in-flows using DeleteChildsNextInFlow()
*/
nsresult
nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nscoord aX,
nscoord aY,
PRUint32 aFlags,
nsReflowStatus& aStatus)
{
NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
@@ -399,8 +580,23 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
#endif
#endif
// Send the WillReflow notification, and reflow the child frame
// Send the WillReflow() notification, and position the child frame
// and its view if requested
aKidFrame->WillReflow(aPresContext);
if (0 == (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
aKidFrame->MoveTo(&aPresContext, aX, aY);
}
if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
nsIView* view;
aKidFrame->GetView(&aPresContext, &view);
if (view) {
PositionFrameView(&aPresContext, aKidFrame, view);
}
}
// Reflow the child frame
result = aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState,
aStatus);
@@ -441,6 +637,97 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
return result;
}
void
nsContainerFrame::PositionChildViews(nsIPresContext* aPresContext,
nsIFrame* aFrame)
{
nsIAtom* childListName = nsnull;
PRInt32 childListIndex = 0;
do {
// Recursively walk aFrame's child frames
nsIFrame* childFrame;
aFrame->FirstChild(childListName, &childFrame);
while (childFrame) {
nsIView* view;
// See if the child frame has a view
childFrame->GetView(aPresContext, &view);
if (view) {
// Position the view. Because any child views are relative to their
// parent, there's no need to recurse
PositionFrameView(aPresContext, childFrame, view);
} else {
// Recursively examine its child frames
PositionChildViews(aPresContext, childFrame);
}
// Get the next sibling child frame
childFrame->GetNextSibling(&childFrame);
}
NS_IF_RELEASE(childListName);
aFrame->GetAdditionalChildListName(childListIndex++, &childListName);
} while (childListName);
}
/**
* The second half of frame reflow. Does the following:
* - sets the frame's bounds
* - sizes and positions (if requested) the frame's view. If the frame's final
* position differs from the current position and the frame itself does not
* have a view, then any child frames with views are positioned so they stay
* in sync
* - sets the view's visibility, opacity, content transparency, and clip
* - invoked the DidReflow() function
*
* Flags:
* NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
* case. Also implies NS_FRAME_NO_MOVE_VIEW
* NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
* don't want to automatically sync the frame and view
* NS_FRAME_NO_SIZE_VIEW - don't size the frame's view
* NS_FRAME_NO_MOVE_CHILD_VIEWS - don't move child views. This is for the case
* where the frame's new position differs from its current position and the
* frame itself doesn't have a view, so moving the frame would cause any child
* views to be out of sync
*/
nsresult
nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
nsIPresContext& aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
nscoord aX,
nscoord aY,
PRUint32 aFlags)
{
nsPoint curOrigin;
nsRect bounds(aX, aY, aDesiredSize.width, aDesiredSize.height);
aKidFrame->GetOrigin(curOrigin);
aKidFrame->SetRect(&aPresContext, bounds);
nsIView* view;
aKidFrame->GetView(&aPresContext, &view);
if (view) {
// Make sure the frame's view is properly sized and positioned and has
// things like opacity correct
SyncFrameViewAfterReflow(&aPresContext, aKidFrame, view,
&aDesiredSize.mCombinedArea,
aFlags);
} else if (0 == (aFlags & NS_FRAME_NO_MOVE_CHILD_VIEWS)) {
// If the frame has moved, then we need to make sure any child views are
// correctly positioned
if ((curOrigin.x != aX) || (curOrigin.y != aY)) {
PositionChildViews(&aPresContext, aKidFrame);
}
}
return aKidFrame->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
}
/**
* Remove and delete aChild's next-in-flow(s). Updates the sibling and flow
* pointers