Bug 552982, Part 3: support panel levels, r=mats

This commit is contained in:
Neil Deakin 2010-07-27 09:38:03 -04:00
Родитель f46c03d388
Коммит 4a99c182d8
11 изменённых файлов: 139 добавлений и 53 удалений

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

@ -377,6 +377,7 @@ GK_ATOM(fixedList, "Fixed-list")
GK_ATOM(flags, "flags")
GK_ATOM(flex, "flex")
GK_ATOM(flexgroup, "flexgroup")
GK_ATOM(floating, "floating")
GK_ATOM(floatList, "Float-list")
GK_ATOM(floor, "floor")
GK_ATOM(focus, "focus")

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

@ -88,7 +88,7 @@
#include "nsIServiceManager.h"
#include "nsThemeConstants.h"
PRInt8 nsMenuPopupFrame::sDefaultLevelParent = -1;
PRInt8 nsMenuPopupFrame::sDefaultLevelIsTop = -1;
// NS_NewMenuPopupFrame
//
@ -125,9 +125,11 @@ nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContex
mHFlip(PR_FALSE),
mVFlip(PR_FALSE)
{
if (sDefaultLevelParent >= 0)
// the preference name is backwards here. True means that the 'top' level is
// the default, and false means that the 'parent' level is the default.
if (sDefaultLevelIsTop >= 0)
return;
sDefaultLevelParent =
sDefaultLevelIsTop =
nsContentUtils::GetBoolPref("ui.panel.default_level_parent", PR_FALSE);
} // ctor
@ -200,7 +202,7 @@ nsMenuPopupFrame::Init(nsIContent* aContent,
}
PRBool
nsMenuPopupFrame::IsNoAutoHide()
nsMenuPopupFrame::IsNoAutoHide() const
{
// Panels with noautohide="true" don't hide when the mouse is clicked
// outside of them, or when another application is made active. Non-autohide
@ -210,29 +212,38 @@ nsMenuPopupFrame::IsNoAutoHide()
nsGkAtoms::_true, eIgnoreCase));
}
PRBool
nsMenuPopupFrame::IsTopMost()
nsPopupLevel
nsMenuPopupFrame::PopupLevel(PRBool aIsNoAutoHide) const
{
// If this panel is not a panel, this is always a top-most popup
// The popup level is determined as follows, in this order:
// 1. non-panels (menus and tooltips) are always topmost
// 2. any specified level attribute
// 3. if this is a noautohide panel, use the 'parent' level
// 4. use the platform-specific default level
// If this is not a panel, this is always a top-most popup.
if (mPopupType != ePopupTypePanel)
return PR_TRUE;
return ePopupLevelTop;
// If this panel is a noautohide panel, it should appear just above the parent
// window.
if (IsNoAutoHide())
return PR_FALSE;
// If the level attribute has been set, use that.
static nsIContent::AttrValuesArray strings[] =
{&nsGkAtoms::top, &nsGkAtoms::parent, &nsGkAtoms::floating, nsnull};
switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::level,
strings, eCaseMatters)) {
case 0:
return ePopupLevelTop;
case 1:
return ePopupLevelParent;
case 2:
return ePopupLevelFloating;
}
// Otherwise, check the topmost attribute.
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::level,
nsGkAtoms::top, eIgnoreCase))
return PR_TRUE;
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::level,
nsGkAtoms::parent, eIgnoreCase))
return PR_FALSE;
// If this panel is a noautohide panel, the default is the parent level.
if (aIsNoAutoHide)
return ePopupLevelParent;
// Otherwise, the result depends on the platform.
return sDefaultLevelParent ? PR_TRUE : PR_FALSE;
return sDefaultLevelIsTop ? ePopupLevelTop : ePopupLevelParent;
}
void
@ -255,6 +266,7 @@ nsMenuPopupFrame::CreateWidgetForView(nsIView* aView)
widgetData.mBorderStyle = eBorderStyle_default;
widgetData.clipSiblings = PR_TRUE;
widgetData.mPopupHint = mPopupType;
widgetData.mNoAutoHide = IsNoAutoHide();
nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(this, this);
PRBool viewHasTransparentContent = !mInContentShell &&
@ -265,12 +277,13 @@ nsMenuPopupFrame::CreateWidgetForView(nsIView* aView)
if (parentContent)
tag = parentContent->Tag();
widgetData.mDropShadow = !(viewHasTransparentContent || tag == nsGkAtoms::menulist);
widgetData.mPopupLevel = PopupLevel(widgetData.mNoAutoHide);
// panels which are not topmost need a parent widget. This allows them to
// panels which have a parent level 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 (!IsTopMost()) {
if (widgetData.mPopupLevel != ePopupLevelTop) {
nsCOMPtr<nsISupports> cont = PresContext()->GetContainer();
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
if (!dsti)
@ -1051,6 +1064,18 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, PRBool aIsMove)
else
screenPoint.x += presContext->CSSPixelsToAppUnits(mXPos);
screenPoint.y += presContext->CSSPixelsToAppUnits(mYPos);
// If this is a noautohide popup, set the screen coordinates of the popup.
// This way, the popup stays at the location where it was opened even when
// the window is moved. Popups at the parent level follow the parent
// window as it is moved and remained anchored, so we want to maintain the
// anchoring instead.
if (IsNoAutoHide() && PopupLevel(PR_TRUE) != ePopupLevelParent) {
// Account for the margin that will end up being added to the screen coordinate
// the next time SetPopupPosition is called.
mScreenXPos = presContext->AppUnitsToIntCSSPixels(screenPoint.x - margin.left);
mScreenYPos = presContext->AppUnitsToIntCSSPixels(screenPoint.y - margin.top);
}
}
else {
// the popup is positioned at a screen coordinate.
@ -1649,9 +1674,13 @@ nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop, PRBool aUpdateAttrs)
// 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.
mScreenXPos = aLeft;
mScreenYPos = aTop;
// using (-1, -1) as coordinates. Subtract off the margin as it will be
// added to the position when SetPopupPosition is called.
nsMargin margin(0, 0, 0, 0);
GetStyleMargin()->GetMargin(margin);
nsPresContext* presContext = PresContext();
mScreenXPos = aLeft - presContext->AppUnitsToIntCSSPixels(margin.left);
mScreenYPos = aTop - presContext->AppUnitsToIntCSSPixels(margin.top);
SetPopupPosition(nsnull, PR_TRUE);

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

@ -183,11 +183,12 @@ public:
// returns true if the popup is a panel with the noautohide attribute set to
// true. These panels do not roll up automatically.
PRBool IsNoAutoHide();
PRBool IsNoAutoHide() const;
// returns true if the popup is a top-most window. Otherwise, the
// panel appears in front of the parent window.
PRBool IsTopMost();
nsPopupLevel PopupLevel() const
{
return PopupLevel(IsNoAutoHide());
}
void EnsureWidget();
@ -319,6 +320,9 @@ public:
protected:
// returns the popup's level.
nsPopupLevel PopupLevel(PRBool aIsNoAutoHide) const;
// redefine to tell the box system not to move the views.
virtual void GetLayoutFlags(PRUint32& aFlags);
@ -400,7 +404,7 @@ protected:
PRPackedBool mHFlip;
PRPackedBool mVFlip;
static PRInt8 sDefaultLevelParent;
static PRInt8 sDefaultLevelIsTop;
}; // class nsMenuPopupFrame
#endif

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

@ -320,7 +320,8 @@ nsXULPopupManager::PopupMoved(nsIView* aView, nsIntPoint aPnt)
// 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()) {
if (menuPopupFrame->IsAnchored() &&
menuPopupFrame->PopupLevel() == ePopupLevelParent) {
menuPopupFrame->SetPopupPosition(nsnull, PR_TRUE);
}
else {

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

@ -71,6 +71,22 @@ enum nsPopupType {
// nsXULPopupManager::GetTopPopup
};
/**
* Popup levels specify the window ordering behaviour.
*/
enum nsPopupLevel {
// the popup appears just above its parent and maintains its position
// relative to the parent
ePopupLevelParent,
// the popup is a floating popup used for tool palettes. A parent window
// must be specified, but a platform implementation need not use this.
// On Windows, floating is generally equivalent to parent. On Mac, floating
// puts the popup at toplevel, but it will hide when the application is deactivated
ePopupLevelFloating,
// the popup appears on top of other windows, including those of other applications
ePopupLevelTop
};
/**
* Border styles
*/
@ -122,12 +138,14 @@ struct nsWidgetInitData {
mBorderStyle(eBorderStyle_default),
mContentType(eContentTypeInherit),
mPopupHint(ePopupTypePanel),
mPopupLevel(ePopupLevelTop),
clipChildren(PR_FALSE),
clipSiblings(PR_FALSE),
mDropShadow(PR_FALSE),
mListenForResizes(PR_FALSE),
mUnicode(PR_TRUE),
mRTL(PR_FALSE)
mRTL(PR_FALSE),
mNoAutoHide(PR_FALSE)
{
}
@ -135,11 +153,13 @@ struct nsWidgetInitData {
nsBorderStyle mBorderStyle;
nsContentType mContentType; // Exposed so screen readers know what's UI
nsPopupType mPopupHint;
nsPopupLevel mPopupLevel;
// when painting exclude area occupied by child windows and sibling windows
PRPackedBool clipChildren, clipSiblings, mDropShadow;
PRPackedBool mListenForResizes;
PRPackedBool mUnicode;
PRPackedBool mRTL;
PRPackedBool mNoAutoHide; // true for noautohide panels
};
#endif // nsWidgetInitData_h__

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

@ -289,6 +289,8 @@ public:
static void UnifiedShading(void* aInfo, const CGFloat* aIn, CGFloat* aOut);
void SetPopupWindowLevel();
protected:
nsresult CreateNativeWindow(const NSRect &aRect,

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

@ -416,7 +416,7 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect,
if (mWindowType == eWindowType_invisible) {
[mWindow setLevel:kCGDesktopWindowLevelKey];
} else if (mWindowType == eWindowType_popup) {
[mWindow setLevel:NSPopUpMenuWindowLevel];
SetPopupWindowLevel();
[mWindow setHasShadow:YES];
}
@ -591,7 +591,7 @@ NS_IMETHODIMP nsCocoaWindow::SetModal(PRBool aState)
delete saved; // "window" not ADDREFed
}
if (mWindowType == eWindowType_popup)
[mWindow setLevel:NSPopUpMenuWindowLevel];
SetPopupWindowLevel();
else
[mWindow setLevel:NSNormalWindowLevel];
}
@ -695,11 +695,12 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
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)
// If a parent window was supplied and this is a popup at the parent
// level, 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 && mPopupLevel == ePopupLevelParent)
[nativeParentWindow addChildWindow:mWindow
ordered:NSWindowAbove];
}
@ -1460,8 +1461,10 @@ NS_IMETHODIMP nsCocoaWindow::CaptureRollupEvents(nsIRollupListener * aListener,
// "active" one is always above any other non-native popup windows that
// may be visible.
if (mWindow && (mWindowType == eWindowType_popup))
[mWindow setLevel:NSPopUpMenuWindowLevel];
SetPopupWindowLevel();
} else {
// XXXndeakin this doesn't make sense.
// Why is the new window assumed to be a modal panel?
if (mWindow && (mWindowType == eWindowType_popup))
[mWindow setLevel:NSModalPanelWindowLevel];
}
@ -1620,6 +1623,22 @@ nsCocoaWindow::UnifiedShading(void* aInfo, const CGFloat* aIn, CGFloat* aOut)
aOut[3] = 1.0f;
}
void nsCocoaWindow::SetPopupWindowLevel()
{
// Floating popups are at the floating level and hide when the window is
// deactivated.
if (mPopupLevel == ePopupLevelFloating) {
[mWindow setLevel:NSFloatingWindowLevel];
[mWindow setHidesOnDeactivate:YES];
}
else {
// Otherwise, this is a top-level or parent popup. Parent popups always
// appear just above their parent and essentially ignore the level.
[mWindow setLevel:NSPopUpMenuWindowLevel];
[mWindow setHidesOnDeactivate:NO];
}
}
@implementation WindowDelegate
// We try to find a gecko menu bar to paint. If one does not exist, just paint
@ -2474,6 +2493,12 @@ ContentPatternDrawCallback(void* aInfo, CGContextRef aContext)
mIsContextMenu = flag;
}
- (BOOL)canBecomeMainWindow
{
// This is overriden because the default is 'yes' when a titlebar is present.
return NO;
}
@end
// According to Apple's docs on [NSWindow canBecomeKeyWindow] and [NSWindow

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

@ -4037,10 +4037,9 @@ nsWindow::Create(nsIWidget *aParent,
}
}
else if (mWindowType == eWindowType_popup) {
// The value of aParent contains a code: If a popup window has a
// non-NULL nsIWidget* aParent, it indicates that it should not be
// above all other windows (e.g a noautohide panel).
if (!aParent) {
// Popups that are not noautohide are only temporary. The are used
// for menus and the like and disappear when another window is used.
if (!aInitData->mNoAutoHide) {
// For most popups, use the standard GtkWindowType
// GTK_WINDOW_POPUP, which will use a Window with the
// override-redirect attribute (for temporary windows).

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

@ -556,11 +556,7 @@ nsWindow::Create(nsIWidget *aParent,
}
if (mWindowType == eWindowType_popup) {
// 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 (!aParent)
parent = NULL;
} else if (mWindowType == eWindowType_invisible) {
// Make sure CreateWindowEx succeeds at creating a toplevel window
@ -896,12 +892,16 @@ DWORD nsWindow::WindowExStyle()
return WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME;
case eWindowType_popup:
return
{
DWORD extendedStyle =
#if defined(WINCE) && !defined(WINCE_WINDOWS_MOBILE)
WS_EX_NOACTIVATE |
#endif
WS_EX_TOPMOST | WS_EX_TOOLWINDOW;
WS_EX_TOOLWINDOW;
if (mPopupLevel == ePopupLevelTop)
extendedStyle |= WS_EX_TOPMOST;
return extendedStyle;
}
default:
NS_ERROR("unknown border style");
// fall through

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

@ -110,6 +110,7 @@ nsBaseWidget::nsBaseWidget()
, mClipRectCount(0)
, mZIndex(0)
, mSizeMode(nsSizeMode_Normal)
, mPopupLevel(ePopupLevelTop)
{
#ifdef NOISY_WIDGET_LEAKS
gNumWidgets++;
@ -214,6 +215,7 @@ void nsBaseWidget::BaseCreate(nsIWidget *aParent,
if (nsnull != aInitData) {
mWindowType = aInitData->mWindowType;
mBorderStyle = aInitData->mBorderStyle;
mPopupLevel = aInitData->mPopupLevel;
}
if (aParent) {

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

@ -156,6 +156,8 @@ public:
NS_IMETHOD GetNonClientMargins(nsIntMargin &margins);
NS_IMETHOD SetNonClientMargins(nsIntMargin &margins);
nsPopupLevel PopupLevel() { return mPopupLevel; }
/**
* Use this when GetLayerManager() returns a BasicLayerManager
* (nsBaseWidget::GetLayerManager() does). This sets up the widget's
@ -228,6 +230,7 @@ protected:
PRUint32 mClipRectCount;
PRInt32 mZIndex;
nsSizeMode mSizeMode;
nsPopupLevel mPopupLevel;
// the last rolled up popup. Only set this when an nsAutoRollup is in scope,
// so it can be cleared automatically.