Bug 562740, combine popup layout methods, fix menulist scrollbar position when list is larger that screen, r=roc

This commit is contained in:
Neil Deakin 2010-05-13 09:42:57 -04:00
parent 443b5d3dfe
commit 2fb544a731
6 changed files with 92 additions and 148 deletions

View File

@ -746,55 +746,9 @@ nsMenuFrame::DoLayout(nsBoxLayoutState& aState)
// lay us out
nsresult rv = nsBoxFrame::DoLayout(aState);
// layout the popup. First we need to get it.
if (mPopupFrame) {
PRBool sizeToPopup = IsSizedToPopup(mContent, PR_FALSE);
// then get its preferred size
nsSize prefSize = mPopupFrame->GetPrefSize(aState);
nsSize minSize = mPopupFrame->GetMinSize(aState);
nsSize maxSize = mPopupFrame->GetMaxSize(aState);
prefSize = BoundsCheck(minSize, prefSize, maxSize);
if (sizeToPopup)
prefSize.width = mRect.width;
// if the pref size changed then set bounds to be the pref size
PRBool sizeChanged = (mPopupFrame->PreferredSize() != prefSize);
if (sizeChanged) {
mPopupFrame->SetPreferredBounds(aState, nsRect(0,0,prefSize.width, prefSize.height));
}
// if the menu has just been opened, or its size changed, position
// the popup. The flag that the popup checks in the HasOpenChanged
// method will get cleared in AdjustView which is called below.
if (IsOpen() && (sizeChanged || mPopupFrame->HasOpenChanged()))
mPopupFrame->SetPopupPosition(this, PR_FALSE);
// is the new size too small? Make sure we handle scrollbars correctly
nsIBox* child = mPopupFrame->GetChildBox();
nsRect bounds(mPopupFrame->GetRect());
nsIScrollableFrame *scrollframe = do_QueryFrame(child);
if (scrollframe &&
scrollframe->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
if (bounds.height < prefSize.height) {
// layout the child
mPopupFrame->Layout(aState);
nsMargin scrollbars = scrollframe->GetActualScrollbarSizes();
if (bounds.width < prefSize.width + scrollbars.left + scrollbars.right)
{
bounds.width += scrollbars.left + scrollbars.right;
mPopupFrame->SetBounds(aState, bounds);
}
}
}
// layout the child
mPopupFrame->Layout(aState);
mPopupFrame->AdjustView();
mPopupFrame->LayoutPopup(aState, this, sizeToPopup);
}
return rv;

View File

@ -369,43 +369,86 @@ nsMenuPopupFrame::IsLeaf() const
}
void
nsMenuPopupFrame::SetPreferredBounds(nsBoxLayoutState& aState,
const nsRect& aRect)
nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu, PRBool aSizedToPopup)
{
nsBox::SetBounds(aState, aRect, PR_FALSE);
mPrefSize = aRect.Size();
// if the popup is not open, only do layout if the menu is sized to the popup
PRBool isOpen = IsOpen();
if (!mGeneratedChildren || (!isOpen && !aSizedToPopup))
return;
// get the preferred, minimum and maximum size. If the menu is sized to the
// popup, then the popup's width is the menu's width.
nsSize prefSize = GetPrefSize(aState);
nsSize minSize = GetMinSize(aState);
nsSize maxSize = GetMaxSize(aState);
if (aSizedToPopup) {
prefSize.width = aParentMenu->GetRect().width;
}
prefSize = BoundsCheck(minSize, prefSize, maxSize);
// if the size changed then set the bounds to be the preferred size
PRBool sizeChanged = (mPrefSize != prefSize);
if (sizeChanged) {
SetBounds(aState, nsRect(0, 0, prefSize.width, prefSize.height), PR_FALSE);
mPrefSize = prefSize;
}
if (isOpen) {
SetPopupPosition(aParentMenu, PR_FALSE);
}
nsRect bounds(GetRect());
Layout(aState);
// if the width or height changed, readjust the popup position. This is a
// special case for tooltips where the preferred height doesn't include the
// real height for its inline element, but does once it is laid out.
// This is bug 228673 which doesn't have a simple fix.
if (!aParentMenu) {
nsSize newsize = GetSize();
if (newsize.width > bounds.width || newsize.height > bounds.height) {
// the size after layout was larger than the preferred size,
// so set the preferred size accordingly
mPrefSize = newsize;
if (isOpen) {
SetPopupPosition(nsnull, PR_FALSE);
}
}
}
if (isOpen) {
AdjustView();
}
}
void
nsMenuPopupFrame::AdjustView()
{
if ((mPopupState == ePopupOpen || mPopupState == ePopupOpenAndVisible) &&
mGeneratedChildren) {
// if the popup has just opened, make sure the scrolled window is at 0,0
if (mIsOpenChanged) {
nsIBox* child = GetChildBox();
nsIScrollableFrame *scrollframe = do_QueryFrame(child);
if (scrollframe)
scrollframe->ScrollTo(nsPoint(0,0), nsIScrollableFrame::INSTANT);
}
// if the popup has just opened, make sure the scrolled window is at 0,0
if (mIsOpenChanged) {
nsIBox* child = GetChildBox();
nsIScrollableFrame *scrollframe = do_QueryFrame(child);
if (scrollframe)
scrollframe->ScrollTo(nsPoint(0,0), nsIScrollableFrame::INSTANT);
}
nsIView* view = GetView();
nsIViewManager* viewManager = view->GetViewManager();
nsRect rect = GetRect();
rect.x = rect.y = 0;
viewManager->ResizeView(view, rect);
viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
mPopupState = ePopupOpenAndVisible;
nsIView* view = GetView();
nsIViewManager* viewManager = view->GetViewManager();
nsRect rect = GetRect();
rect.x = rect.y = 0;
viewManager->ResizeView(view, rect);
viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
mPopupState = ePopupOpenAndVisible;
nsPresContext* pc = PresContext();
nsContainerFrame::SyncFrameViewProperties(pc, this, nsnull, view, 0);
nsPresContext* pc = PresContext();
nsContainerFrame::SyncFrameViewProperties(pc, this, nsnull, view, 0);
// fire popupshown event when the state has changed
if (mIsOpenChanged) {
mIsOpenChanged = PR_FALSE;
nsCOMPtr<nsIRunnable> event = new nsXULPopupShownEvent(GetContent(), pc);
NS_DispatchToCurrentThread(event);
}
// fire popupshown event when the state has changed
if (mIsOpenChanged) {
mIsOpenChanged = PR_FALSE;
nsCOMPtr<nsIRunnable> event = new nsXULPopupShownEvent(GetContent(), pc);
NS_DispatchToCurrentThread(event);
}
}

View File

@ -199,8 +199,10 @@ public:
virtual PRBool IsLeaf() const;
// AdjustView should be called by the parent frame after the popup has been
// laid out, so that the view can be shown.
// layout, position and display the popup as needed
void LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu, PRBool aSizedToPopup);
// AdjustView is called by LayoutPopup to position and show the popup's view.
void AdjustView();
nsIView* GetRootViewForPopup(nsIFrame* aStartFrame);
@ -226,7 +228,6 @@ public:
nsPopupType PopupType() const { return mPopupType; }
PRBool IsMenu() { return mPopupType == ePopupTypeMenu; }
PRBool IsOpen() { return mPopupState == ePopupOpen || mPopupState == ePopupOpenAndVisible; }
PRBool HasOpenChanged() { return mIsOpenChanged; }
// returns true if the popup is in a content shell, or false for a popup in
// a chrome shell
@ -291,14 +292,6 @@ public:
nsIScrollableFrame* GetScrollFrame(nsIFrame* aStart);
// same as SetBounds except the preferred size mPrefSize is also set.
void SetPreferredBounds(nsBoxLayoutState& aState, const nsRect& aRect);
// retrieve the last preferred size
nsSize PreferredSize() { return mPrefSize; }
// set the last preferred size
void SetPreferredSize(nsSize aSize) { mPrefSize = aSize; }
// For a popup that should appear at the given anchor point, determine
// the screen area that it is constrained by. This will be the available
// area of the screen the popup should be displayed on. Content popups,
@ -369,9 +362,6 @@ protected:
// mRect in the case where the popup was resized because it was too large
// for the screen. The preferred size mPrefSize holds the full size the popup
// would be before resizing. Computations are performed using this size.
// The parent frame is responsible for setting the preferred size using
// SetPreferredBounds or SetPreferredSize before positioning the popup with
// SetPopupPosition.
nsSize mPrefSize;
// the position of the popup. The screen coordinates, if set to values other

View File

@ -155,54 +155,7 @@ nsPopupSetFrame::DoLayout(nsBoxLayoutState& aState)
// lay out all of our currently open popups.
for (nsFrameList::Enumerator e(mPopupList); !e.AtEnd(); e.Next()) {
nsMenuPopupFrame* popupChild = static_cast<nsMenuPopupFrame*>(e.get());
if (popupChild->IsOpen()) {
// then get its preferred size
nsSize prefSize = popupChild->GetPrefSize(aState);
nsSize minSize = popupChild->GetMinSize(aState);
nsSize maxSize = popupChild->GetMaxSize(aState);
prefSize = BoundsCheck(minSize, prefSize, maxSize);
popupChild->SetPreferredBounds(aState, nsRect(0,0,prefSize.width, prefSize.height));
popupChild->SetPopupPosition(nsnull, PR_FALSE);
// is the new size too small? Make sure we handle scrollbars correctly
nsIBox* child = popupChild->GetChildBox();
nsRect bounds(popupChild->GetRect());
nsIScrollableFrame *scrollframe = do_QueryFrame(child);
if (scrollframe &&
scrollframe->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
// if our pref height
if (bounds.height < prefSize.height) {
// layout the child
popupChild->Layout(aState);
nsMargin scrollbars = scrollframe->GetActualScrollbarSizes();
if (bounds.width < prefSize.width + scrollbars.left + scrollbars.right)
{
bounds.width += scrollbars.left + scrollbars.right;
popupChild->SetBounds(aState, bounds);
}
}
}
// layout the child
popupChild->Layout(aState);
// if the width or height changed, readjust the popup position. This is a
// special case for tooltips where the preferred height doesn't include the
// real height for its inline element, but does once it is laid out.
// This is bug 228673 which doesn't have a simple fix.
if (popupChild->GetRect().width > bounds.width ||
popupChild->GetRect().height > bounds.height) {
// the size after layout was larger than the preferred size,
// so set the preferred size accordingly
popupChild->SetPreferredSize(popupChild->GetSize());
popupChild->SetPopupPosition(nsnull, PR_FALSE);
}
popupChild->AdjustView();
}
popupChild->LayoutPopup(aState, nsnull, PR_FALSE);
}
return rv;

View File

@ -279,20 +279,17 @@ nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent,
nsIAtom* aFrameType,
PRBool aShouldFlush)
{
nsIDocument *document = aContent->GetCurrentDoc();
if (document) {
nsCOMPtr<nsIPresShell> presShell = document->GetPrimaryShell();
if (presShell) {
if (aShouldFlush)
if (aShouldFlush) {
nsIDocument *document = aContent->GetCurrentDoc();
if (document) {
nsCOMPtr<nsIPresShell> presShell = document->GetPrimaryShell();
if (presShell)
presShell->FlushPendingNotifications(Flush_Frames);
nsIFrame* frame = aContent->GetPrimaryFrame();
if (frame && frame->GetType() == aFrameType)
return frame;
}
}
return nsnull;
nsIFrame* frame = aContent->GetPrimaryFrame();
return (frame && frame->GetType() == aFrameType) ? frame : nsnull;
}
nsMenuFrame*

View File

@ -82,9 +82,16 @@ function differentPressed()
// now make sure that using a key scrolls the menu correctly
gShowPopup = true;
for (let i = 0; i < 15; i++) {
for (let i = 0; i < 65; i++) {
list.appendItem("Item" + i, "item" + i);
}
list.open = true;
is(list.getBoundingClientRect().width, list.firstChild.getBoundingClientRect().width,
"menu and popup width match");
ok(list.getBoundingClientRect().width > list.getItemAtIndex(0).getBoundingClientRect().width + 2,
"menuitem width accounts for scrollbar");
list.open = false;
list.menupopup.maxHeight = 100;
list.open = true;