Bug 395334, allow noautohide panels to have a parent so they that float and move with their parent, also fixes bug 395123, r=josh,roc,sr=roc

This commit is contained in:
enndeakin@sympatico.ca 2007-11-28 12:18:11 -08:00
Родитель f0f2e9808f
Коммит 004c87805a
14 изменённых файлов: 180 добавлений и 80 удалений

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

@ -218,4 +218,8 @@
#define NS_DOMSTORAGEMANAGER_CID \
{ 0xb88a4712, 0xeb52, 0x4c10, { 0x9b, 0x85, 0xbf, 0x58, 0x94, 0xb5, 0x10, 0xf0 } }
// {14632191-AC21-4BDF-83E7-2363AD17E838}
#define NS_XULPOPUPMANAGER_CID \
{ 0x14632191, 0xac21, 0x4bdf, { 0x83, 0xe7, 0x23, 0x63, 0xad, 0x17, 0xe8, 0x38 } }
#endif /* nsLayoutCID_h__ */

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

@ -99,6 +99,7 @@
#include "nsLayoutCID.h"
#include "nsILanguageAtomService.h"
#include "nsStyleSheetService.h"
#include "nsXULPopupManager.h"
// Transformiix stuff
#include "nsXPathEvaluator.h"
@ -506,6 +507,7 @@ MAKE_CTOR(CreateXULSortService, nsIXULSortService, NS_NewXUL
MAKE_CTOR(CreateXULDocument, nsIXULDocument, NS_NewXULDocument)
// NS_NewXULControllers
// NS_NewXULPrototypeCache
MAKE_CTOR(CreateXULPopupManager, nsISupports, NS_NewXULPopupManager)
#endif
#ifdef MOZ_XTF
MAKE_CTOR(CreateXTFService, nsIXTFService, NS_NewXTFService)
@ -1176,6 +1178,11 @@ static const nsModuleComponentInfo gComponents[] = {
"@mozilla.org/xul/xul-tree-builder;1",
NS_NewXULTreeBuilder },
{ "XUL Popup Manager",
NS_XULPOPUPMANAGER_CID,
"@mozilla.org/xul/xul-popup-manager;1",
CreateXULPopupManager },
{ "XUL Document",
NS_XULDOCUMENT_CID,
"@mozilla.org/xul/xul-document;1",

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

@ -737,4 +737,7 @@ protected:
nsMenuPopupFrame* mTimerMenu;
};
nsresult
NS_NewXULPopupManager(nsISupports** aResult);
#endif

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

@ -80,6 +80,8 @@
#include "nsIPopupBoxObject.h"
#include "nsIReflowCallback.h"
#include "nsBindingManager.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIBaseWindow.h"
#ifdef XP_WIN
#include "nsISound.h"
#endif
@ -174,6 +176,17 @@ nsMenuPopupFrame::Init(nsIContent* aContent,
return rv;
}
PRBool
nsMenuPopupFrame::IsNoAutoHide()
{
// Panels with noautohide="true" don't hide when the mouse is clicked
// outside of them, or when another application is made active. Non-autohide
// panels cannot be used in content windows.
return (!mInContentShell && mPopupType == ePopupTypePanel &&
mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautohide,
nsGkAtoms::_true, eIgnoreCase));
}
void
nsMenuPopupFrame::EnsureWidget()
{
@ -201,14 +214,34 @@ nsMenuPopupFrame::CreateWidgetForView(nsIView* aView)
if (parentContent)
tag = parentContent->Tag();
widgetData.mDropShadow = !(viewHasTransparentContent || tag == nsGkAtoms::menulist);
// panels which don't auto-hide need a parent widget. This allows them
// to always appear in front of the parent window but behind other windows
// that should be in front of it.
nsCOMPtr<nsIWidget> parentWidget;
if (IsNoAutoHide()) {
nsCOMPtr<nsISupports> cont = PresContext()->GetContainer();
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
if (!dsti)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
dsti->GetTreeOwner(getter_AddRefs(treeOwner));
if (!treeOwner) return NS_ERROR_FAILURE;
nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(treeOwner));
if (baseWindow)
baseWindow->GetMainWidget(getter_AddRefs(parentWidget));
}
#if defined(XP_MACOSX) || defined(XP_BEOS)
static NS_DEFINE_IID(kCPopupCID, NS_POPUP_CID);
aView->CreateWidget(kCPopupCID, &widgetData, nsnull, PR_TRUE, PR_TRUE,
eContentTypeUI);
eContentTypeUI, parentWidget);
#else
static NS_DEFINE_IID(kCChildCID, NS_CHILD_CID);
aView->CreateWidget(kCChildCID, &widgetData, nsnull, PR_TRUE, PR_TRUE);
aView->CreateWidget(kCChildCID, &widgetData, nsnull, PR_TRUE, PR_TRUE,
eContentTypeInherit, parentWidget);
#endif
aView->GetWidget()->SetWindowTranslucency(viewHasTransparentContent);
return NS_OK;
@ -1638,10 +1671,10 @@ nsMenuPopupFrame::MoveToInternal(PRInt32 aLeft, PRInt32 aTop)
widget->Move(aLeft - screenPos.x, aTop - screenPos.y);
}
void
nsMenuPopupFrame::GetAutoPosition(PRBool* aShouldAutoPosition)
PRBool
nsMenuPopupFrame::GetAutoPosition()
{
*aShouldAutoPosition = mShouldAutoPosition;
return mShouldAutoPosition;
}
void

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

@ -178,6 +178,10 @@ public:
nscoord aX, nscoord aY, nsIFrame* aForChild,
PRBool aImmediate);
// returns true if the popup is a panel with the noautohide attribute set to
// true. These panels do not roll up automatically.
PRBool IsNoAutoHide();
void EnsureWidget();
virtual nsresult CreateWidgetForView(nsIView* aView);
@ -264,7 +268,7 @@ public:
// May kill the frame.
void MoveTo(PRInt32 aLeft, PRInt32 aTop);
void GetAutoPosition(PRBool* aShouldAutoPosition);
PRBool GetAutoPosition();
void SetAutoPosition(PRBool aShouldAutoPosition);
void SetConsumeRollupEvent(PRUint32 aConsumeMode);

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

@ -175,7 +175,7 @@ nsPopupBoxObject::GetAutoPosition(PRBool* aShouldAutoPosition)
{
nsMenuPopupFrame *menuPopupFrame = GetMenuPopupFrame();
if (menuPopupFrame) {
menuPopupFrame->GetAutoPosition(aShouldAutoPosition);
*aShouldAutoPosition = menuPopupFrame->GetAutoPosition();
}
return NS_OK;

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

@ -211,6 +211,23 @@ nsXULPopupManager::GetSubmenuWidgetChain(nsISupportsArray **_retval)
return NS_OK;
}
NS_IMETHODIMP
nsXULPopupManager::AdjustPopupsOnWindowChange()
{
// Panels are moved and kept aligned with the anchor when the parent window
// moves. We only look through the panels list, as menus are expected to
// roll up when a window is moved, so menus don't need to be moved.
nsMenuChainItem* item = mPanels;
while (item) {
// if the auto positioning has been disabled, don't move the popup
if (item->Frame()->GetAutoPosition())
item->Frame()->SetPopupPosition(nsnull);
item = item->GetParent();
}
return NS_OK;
}
nsIFrame*
nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent,
nsIAtom* aFrameType,
@ -468,10 +485,7 @@ nsXULPopupManager::ShowPopupCallback(nsIContent* aPopup,
// the noautohide attribute to disable this behaviour. It is expected
// that the application will hide these popups manually. The tooltip
// listener will handle closing the tooltip also.
if (popupType == ePopupTypeTooltip ||
(popupType == ePopupTypePanel &&
aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautohide,
nsGkAtoms::_true, eIgnoreCase))) {
if (aPopupFrame->IsNoAutoHide() || popupType == ePopupTypeTooltip) {
item->SetParent(mPanels);
mPanels = item;
}
@ -1942,3 +1956,12 @@ nsXULMenuCommandEvent::Run()
return NS_OK;
}
nsresult
NS_NewXULPopupManager(nsISupports** aResult)
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
NS_IF_ADDREF(pm);
*aResult = static_cast<nsIMenuRollup *>(pm);
return NS_OK;
}

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

@ -59,10 +59,10 @@ enum nsViewVisibility {
};
// IID for the nsIView interface
// 1b0215f7-f5d1-4574-9ab9-abdcceddb82e
// 1377A30E-99E6-42FA-9A2E-EEEC6B31B7B6
#define NS_IVIEW_IID \
{ 0x1b0215f7, 0xf5d1, 0x4574, \
{ 0x9a, 0xb9, 0xab, 0xdc, 0xce, 0xdd, 0xb8, 0x2e } }
{ 0x1377ae0e, 0x99e6, 0x42fa, \
{ 0x9a, 0x2e, 0xee, 0xec, 0x6b, 0x31, 0xb7, 0xb6 } }
// Public view flags are defined in this file
#define NS_VIEW_FLAGS_PUBLIC 0x00FF
@ -281,6 +281,8 @@ public:
* @param aWindowType is either content, UI or inherit from parent window.
* This is used to expose what type of window this is to
* assistive technology like screen readers.
* @param aParentWidget alternative parent to aNative used for popups. Must
* be null for non-popups.
* @return error status
*/
nsresult CreateWidget(const nsIID &aWindowIID,
@ -288,7 +290,8 @@ public:
nsNativeWidget aNative = nsnull,
PRBool aEnableDragDrop = PR_TRUE,
PRBool aResetVisibility = PR_TRUE,
nsContentType aWindowType = eContentTypeInherit);
nsContentType aWindowType = eContentTypeInherit,
nsIWidget* aParentWidget = nsnull);
/**
* In 4.0, the "cutout" nature of a view is queryable.

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

@ -606,7 +606,8 @@ nsresult nsIView::CreateWidget(const nsIID &aWindowIID,
nsNativeWidget aNative,
PRBool aEnableDragDrop,
PRBool aResetVisibility,
nsContentType aContentType)
nsContentType aContentType,
nsIWidget* aParentWidget)
{
if (NS_UNLIKELY(mWindow)) {
NS_ERROR("We already have a window for this view? BAD");
@ -643,26 +644,34 @@ nsresult nsIView::CreateWidget(const nsIID &aWindowIID,
}
aWidgetInitData->mContentType = aContentType;
if (aNative)
if (aNative && aWidgetInitData->mWindowType != eWindowType_popup)
mWindow->Create(aNative, trect, ::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
else
{
if (!initDataPassedIn && GetParent() &&
GetParent()->GetViewManager() != mViewManager)
initData.mListenForResizes = PR_TRUE;
nsIWidget* parentWidget = GetParent() ? GetParent()->GetNearestWidget(nsnull)
: nsnull;
if (aWidgetInitData->mWindowType == eWindowType_popup) {
// Without a parent, we can't make a popup. This can happen
// when printing
if (!parentWidget)
return NS_ERROR_FAILURE;
mWindow->Create(parentWidget->GetNativeData(NS_NATIVE_WIDGET), trect,
::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
} else {
mWindow->Create(parentWidget, trect,
if (aParentWidget) {
NS_ASSERTION(aWidgetInitData->mWindowType == eWindowType_popup,
"popup widget type expected");
mWindow->Create(aParentWidget, trect,
::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
}
else {
nsIWidget* parentWidget = GetParent() ? GetParent()->GetNearestWidget(nsnull)
: nsnull;
if (aWidgetInitData->mWindowType == eWindowType_popup) {
// Without a parent, we can't make a popup. This can happen
// when printing
if (!parentWidget)
return NS_ERROR_FAILURE;
mWindow->Create(parentWidget->GetNativeData(NS_NATIVE_WIDGET), trect,
::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
} else {
mWindow->Create(parentWidget, trect,
::HandleEvent, dx, nsnull, nsnull, aWidgetInitData);
}
}
}
if (aEnableDragDrop) {
mWindow->EnableDragDrop(PR_TRUE);

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

@ -41,12 +41,15 @@
#include "nsISupportsArray.idl"
[uuid(05c48880-0fcf-11d4-bb6f-d9f289fe803c)]
[uuid(10C69225-2C5A-4948-8690-0BC589D145B4)]
interface nsIMenuRollup : nsISupports
{
// Walks up the menu parent chain of a submenu pulling out the widgets and
// places them into a list. Useful for determining if a click is in a
// parent menu.
nsISupportsArray GetSubmenuWidgetChain ( ) ;
// adjust the position of any open popups when the position or size of a
// window has been changed.
void AdjustPopupsOnWindowChange ( );
};

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

@ -547,6 +547,14 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
object:@"org.mozilla.gecko.PopupWindow"];
}
// if a parent was supplied, set its child window. This will cause the
// child window to appear above the parent and move when the parent
// does. Setting this needs to happen after the _setWindowNumber calls
// above, otherwise the window doesn't focus properly.
if (nativeParentWindow)
[nativeParentWindow addChildWindow:mWindow
ordered:NSWindowAbove];
}
else {
mVisible = PR_TRUE;
@ -624,8 +632,15 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
// the NSApplication class (in header files generated using class-dump).
// This workaround was "borrowed" from the Java Embedding Plugin (which
// uses it for a different purpose).
if (mWindowType == eWindowType_popup)
if (mWindowType == eWindowType_popup) {
// remove the window as a child of its parent again. This will just
// do nothing if the popup was never added as a child.
if (nativeParentWindow)
[nativeParentWindow removeChildWindow:mWindow];
[NSApp _removeWindowFromCache:mWindow];
}
// it's very important to turn off mouse moved events when hiding a window, otherwise
// the windows' tracking rects will interfere with each other. (bug 356528)
[mWindow setAcceptsMouseMovedEvents:NO];
@ -696,17 +711,6 @@ NS_IMETHODIMP nsCocoaWindow::Move(PRInt32 aX, PRInt32 aY)
if (!mWindow || (mBounds.x == aX && mBounds.y == aY))
return NS_OK;
// if we're a popup, we have to convert from our parent widget's coord
// system to the global coord system first because the (x,y) we're given
// is in its coordinate system.
if (mParent && mWindowType == eWindowType_popup) {
nsRect globalRect;
nsRect localRect(aX, aY, 0, 0);
mParent->WidgetToScreen(localRect, globalRect);
aX = globalRect.x;
aY = globalRect.y;
}
// The point we have is in Gecko coordinates (origin top-left). Convert
// it to Cocoa ones (origin bottom-left).
NSPoint coord = {aX, FlippedScreenY(aY)};

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

@ -613,16 +613,7 @@ nsWindow::Move(PRInt32 aX, PRInt32 aY)
return NS_OK;
if (mIsTopLevel) {
if (mParent && mWindowType == eWindowType_popup) {
nsRect oldrect, newrect;
oldrect.x = aX;
oldrect.y = aY;
mParent->WidgetToScreen(oldrect, newrect);
gtk_window_move(GTK_WINDOW(mShell), newrect.x, newrect.y);
}
else {
gtk_window_move(GTK_WINDOW(mShell), aX, aY);
}
gtk_window_move(GTK_WINDOW(mShell), aX, aY);
}
else if (mDrawingarea) {
moz_drawingarea_move(mDrawingarea, aX, aY);
@ -2842,8 +2833,6 @@ nsWindow::NativeCreate(nsIWidget *aParent,
aInitData->mWindowType == eWindowType_invisible) ?
nsnull : aParent;
NS_ASSERTION(aInitData->mWindowType != eWindowType_popup ||
!aParent, "Popups should not be hooked into nsIWidget hierarchy");
NS_ASSERTION(!mWindowGroup, "already have window group (leaking it)");
// initialize all the common bits of this class
@ -2982,8 +2971,16 @@ nsWindow::NativeCreate(nsIWidget *aParent,
}
}
else if (mWindowType == eWindowType_popup) {
mShell = gtk_window_new(GTK_WINDOW_POPUP);
gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup", cBrand.get());
// treat popups with a parent as top level windows
if (mParent) {
mShell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel", cBrand.get());
gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
}
else {
mShell = gtk_window_new(GTK_WINDOW_POPUP);
gtk_window_set_wmclass(GTK_WINDOW(mShell), "Popup", cBrand.get());
}
if (topLevelParent) {
gtk_window_set_transient_for(GTK_WINDOW(mShell),
@ -3297,26 +3294,15 @@ nsWindow::NativeResize(PRInt32 aX, PRInt32 aY,
ResizeTransparencyBitmap(aWidth, aHeight);
if (mIsTopLevel) {
if (mParent && mWindowType == eWindowType_popup) {
nsRect oldrect, newrect;
oldrect.x = aX;
oldrect.y = aY;
mParent->WidgetToScreen(oldrect, newrect);
moz_drawingarea_resize(mDrawingarea, aWidth, aHeight);
gtk_window_move(GTK_WINDOW(mShell), newrect.x, newrect.y);
gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
}
else {
// We only move the toplevel window if someone has
// actually placed the window somewhere. If no placement
// has taken place, we just let the window manager Do The
// Right Thing.
if (mPlaced)
gtk_window_move(GTK_WINDOW(mShell), aX, aY);
// We only move the toplevel window if someone has
// actually placed the window somewhere. If no placement
// has taken place, we just let the window manager Do The
// Right Thing.
if (mPlaced)
gtk_window_move(GTK_WINDOW(mShell), aX, aY);
gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
moz_drawingarea_resize(mDrawingarea, aWidth, aHeight);
}
gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight);
moz_drawingarea_resize(mDrawingarea, aWidth, aHeight);
}
else if (mContainer) {
GtkAllocation allocation;

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

@ -1366,9 +1366,10 @@ nsWindow::StandardWindowCreate(nsIWidget *aParent,
DWORD extendedStyle = WindowExStyle();
if (mWindowType == eWindowType_popup) {
NS_ASSERTION(!aParent, "Popups should not be hooked into the nsIWidget hierarchy");
// Don't set the parent of a popup window.
parent = NULL;
// if a parent was specified, don't use WS_EX_TOPMOST so that the popup
// only appears above the parent, instead of all windows
if (aParent)
extendedStyle = WS_EX_TOOLWINDOW;
} else if (nsnull != aInitData) {
// See if the caller wants to explictly set clip children and clip siblings
if (aInitData->clipChildren) {
@ -1750,7 +1751,8 @@ NS_METHOD nsWindow::Show(PRBool bState)
// activate the popup or clicks will not be sent.
flags |= SWP_NOACTIVATE;
#endif
::SetWindowPos(mWnd, HWND_TOPMOST, 0, 0, 0, 0, flags);
HWND owner = ::GetWindow(mWnd, GW_OWNER);
::SetWindowPos(mWnd, owner ? 0 : HWND_TOPMOST, 0, 0, 0, 0, flags);
} else {
::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
}

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

@ -75,6 +75,7 @@
#include "nsIDOMNodeList.h"
#include "nsITimer.h"
#include "nsXULPopupManager.h"
#include "prmem.h"
#include "prlock.h"
@ -306,12 +307,30 @@ 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.
nsCOMPtr<nsIMenuRollup> pm =
do_GetService("@mozilla.org/xul/xul-popup-manager;1");
if (pm)
pm->AdjustPopupsOnWindowChange();
#endif
// persist position, but not immediately, in case this OS is firing
// repeated move events as the user drags the window
eventWindow->SetPersistenceTimer(PAD_POSITION);
break;
}
case NS_SIZE: {
#ifndef XP_MACOSX
nsCOMPtr<nsIMenuRollup> pm =
do_GetService("@mozilla.org/xul/xul-popup-manager;1");
if (pm)
pm->AdjustPopupsOnWindowChange();
#endif
nsSizeEvent* sizeEvent = (nsSizeEvent*)aEvent;
nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(docShell));
shellAsWin->SetPositionAndSize(0, 0, sizeEvent->windowSize->width,