зеркало из https://github.com/mozilla/gecko-dev.git
Bug 552982, Part 2: update the popup when it is moved or resized natively, r=mats
This commit is contained in:
Родитель
818d6e295a
Коммит
f46c03d388
|
@ -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));
|
||||
|
|
Загрузка…
Ссылка в новой задаче