Bug 552982, Part 2: update the popup when it is moved or resized natively, r=mats

This commit is contained in:
Neil Deakin 2010-07-27 09:38:02 -04:00
Родитель 818d6e295a
Коммит f46c03d388
6 изменённых файлов: 129 добавлений и 21 удалений

Просмотреть файл

@ -76,6 +76,7 @@ class nsMenuBarFrame;
class nsMenuParent;
class nsIDOMKeyEvent;
class nsIDocShellTreeItem;
class nsIView;
// when a menu command is executed, the closemenu attribute may be used
// to define how the menu should be closed up
@ -519,6 +520,18 @@ public:
*/
PRBool MayShowPopup(nsMenuPopupFrame* aFrame);
/**
* Indicate that the popup associated with aView has been moved to the
* specified screen coordiates.
*/
void PopupMoved(nsIView* aView, nsIntPoint aPoint);
/**
* Indicate that the popup associated with aView has been resized to the
* specified screen width and height.
*/
void PopupResized(nsIView* aView, nsIntSize ASize);
/**
* Called when a popup frame is destroyed. In this case, just remove the
* item and later popups from the list. No point going through HidePopup as

Просмотреть файл

@ -968,14 +968,7 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, PRBool aIsMove)
// might be a different document so its presshell must be used.
if (!aAnchorFrame) {
if (mAnchorContent) {
nsCOMPtr<nsIDocument> document = mAnchorContent->GetDocument();
if (document) {
nsIPresShell *shell = document->GetShell();
if (!shell)
return NS_ERROR_FAILURE;
aAnchorFrame = mAnchorContent->GetPrimaryFrame();
}
aAnchorFrame = mAnchorContent->GetPrimaryFrame();
}
if (!aAnchorFrame) {
@ -1651,6 +1644,9 @@ nsMenuPopupFrame::DestroyFrom(nsIFrame* aDestructRoot)
void
nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop, PRBool aUpdateAttrs)
{
if (mScreenXPos == aLeft && mScreenYPos == aTop)
return;
// reposition the popup at the specified coordinates. Don't clear the anchor
// and position, because the popup can be reset to its anchor position by
// using (-1, -1) as coordinates.

Просмотреть файл

@ -311,6 +311,12 @@ public:
// can be taken into account.
void CanAdjustEdges(PRInt8 aHorizontalSide, PRInt8 aVerticalSide, nsIntPoint& aChange);
// Return true if the popup is positioned relative to an anchor.
PRBool IsAnchored() const { return mScreenXPos == -1 && mScreenYPos == -1; }
// Return the screen coordinates of the popup, or (-1, -1) if anchored.
nsIntPoint ScreenPosition() const { return nsIntPoint(mScreenXPos, mScreenYPos); }
protected:
// redefine to tell the box system not to move the views.

Просмотреть файл

@ -262,9 +262,9 @@ nsXULPopupManager::GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain)
void
nsXULPopupManager::AdjustPopupsOnWindowChange(nsPIDOMWindow* aWindow)
{
// Panels with noautohide="true" are moved and kept aligned with the anchor
// when the parent window moves. Dismissable menus and panels are expected
// to roll up when a window is moved, so there is no need to check these.
// When the parent window is moved, adjust any child popups. Dismissable
// menus and panels are expected to roll up when a window is moved, so there
// is no need to check these popups, only the noautohide popups.
nsMenuChainItem* item = mNoHidePanels;
while (item) {
// only move popups that are within the same window and where auto
@ -290,6 +290,67 @@ nsXULPopupManager::AdjustPopupsOnWindowChange(nsPIDOMWindow* aWindow)
}
}
static
nsMenuPopupFrame* GetPopupToMoveOrResize(nsIView* aView)
{
nsIFrame *frame = static_cast<nsIFrame *>(aView->GetClientData());
if (!frame || frame->GetType() != nsGkAtoms::menuPopupFrame)
return nsnull;
// no point moving or resizing hidden popups
nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame *>(frame);
if (menuPopupFrame->PopupState() != ePopupOpenAndVisible)
return nsnull;
return menuPopupFrame;
}
void
nsXULPopupManager::PopupMoved(nsIView* aView, nsIntPoint aPnt)
{
nsMenuPopupFrame* menuPopupFrame = GetPopupToMoveOrResize(aView);
if (!menuPopupFrame)
return;
// Don't do anything if the popup is already at the specified location. This
// prevents recursive calls when a popup is positioned.
nsIntPoint currentPnt = menuPopupFrame->ScreenPosition();
if (aPnt.x != currentPnt.x || aPnt.y != currentPnt.y) {
// Update the popup's position using SetPopupPosition if the popup is
// anchored and at the parent level as these maintain their position
// relative to the parent window. Otherwise, just update the popup to
// the specified screen coordinates.
if (menuPopupFrame->IsAnchored()) {
menuPopupFrame->SetPopupPosition(nsnull, PR_TRUE);
}
else {
menuPopupFrame->MoveTo(aPnt.x, aPnt.y, PR_FALSE);
}
}
}
void
nsXULPopupManager::PopupResized(nsIView* aView, nsIntSize aSize)
{
nsMenuPopupFrame* menuPopupFrame = GetPopupToMoveOrResize(aView);
if (!menuPopupFrame)
return;
nsPresContext* presContext = menuPopupFrame->PresContext();
nsSize currentSize = menuPopupFrame->GetSize();
if (aSize.width != presContext->AppUnitsToDevPixels(currentSize.width) ||
aSize.height != presContext->AppUnitsToDevPixels(currentSize.height)) {
// for resizes, we just set the width and height attributes
nsIContent* popup = menuPopupFrame->GetContent();
nsAutoString width, height;
width.AppendInt(aSize.width);
height.AppendInt(aSize.height);
popup->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, PR_FALSE);
popup->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, PR_TRUE);
}
}
nsIFrame*
nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent,
nsIAtom* aFrameType,

Просмотреть файл

@ -59,6 +59,7 @@
#include "nsThreadUtils.h"
#include "nsContentUtils.h"
#include "nsIPluginWidget.h"
#include "nsXULPopupManager.h"
static NS_DEFINE_IID(kRegionCID, NS_REGION_CID);
@ -717,6 +718,19 @@ void nsViewManager::UpdateViews(nsView *aView, PRUint32 aUpdateFlags)
}
}
static PRBool
IsViewForPopup(nsIView* aView)
{
nsIWidget* widget = aView->GetWidget();
if (widget) {
nsWindowType type;
widget->GetWindowType(type);
return (type == eWindowType_popup);
}
return PR_FALSE;
}
NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
nsIView* aView, nsEventStatus *aStatus)
{
@ -745,9 +759,35 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
NSIntPixelsToAppUnits(height, p2a));
*aStatus = nsEventStatus_eConsumeNoDefault;
}
else if (IsViewForPopup(aView))
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
{
pm->PopupResized(aView, nsIntSize(width, height));
*aStatus = nsEventStatus_eConsumeNoDefault;
}
}
}
}
break;
case NS_MOVE:
{
// A popup's parent view is the root view for the parent window, so when
// a popup moves, the popup's frame and view position must be updated
// to match.
if (aView && IsViewForPopup(aView))
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
{
pm->PopupMoved(aView, aEvent->refPoint);
*aStatus = nsEventStatus_eConsumeNoDefault;
}
}
break;
}
case NS_WILL_PAINT:
@ -890,14 +930,12 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
case NS_CREATE:
case NS_DESTROY:
case NS_SETZLEVEL:
case NS_MOVE:
/* Don't pass these events through. Passing them through
causes performance problems on pages with lots of views/frames
@see bug 112861 */
*aStatus = nsEventStatus_eConsumeNoDefault;
break;
case NS_DISPLAYCHANGED:
//Destroy the cached backbuffer to force a new backbuffer

Просмотреть файл

@ -319,18 +319,14 @@ nsWebShellWindow::HandleEvent(nsGUIEvent *aEvent)
* client area of the window...
*/
case NS_MOVE: {
#ifndef XP_MACOSX
// Move any popups that are attached to their parents. That is, the
// popup moves along with the parent window when it moves. This
// doesn't need to happen on Mac, as Cocoa provides a nice API
// which does this for us.
// Adjust any child popups so that their widget offsets and coordinates
// are correct with respect to the new position of the window
nsCOMPtr<nsIMenuRollup> pm =
do_GetService("@mozilla.org/xul/xul-popup-manager;1");
if (pm) {
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(docShell);
pm->AdjustPopupsOnWindowChange(window);
}
#endif
// persist position, but not immediately, in case this OS is firing
// repeated move events as the user drags the window
@ -338,14 +334,12 @@ nsWebShellWindow::HandleEvent(nsGUIEvent *aEvent)
break;
}
case NS_SIZE: {
#ifndef XP_MACOSX
nsCOMPtr<nsIMenuRollup> pm =
do_GetService("@mozilla.org/xul/xul-popup-manager;1");
if (pm) {
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(docShell);
pm->AdjustPopupsOnWindowChange(window);
}
#endif
nsSizeEvent* sizeEvent = (nsSizeEvent*)aEvent;
nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(docShell));