mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 392160, improve popup positioning to not use widget calculations, r=sharparrow, sr=bz, a=dbaron
This commit is contained in:
parent
7e3a74443c
commit
d6dd5b99e5
@ -598,71 +598,37 @@ nsMenuPopupFrame::GetLayoutFlags(PRUint32& aFlags)
|
||||
aFlags = NS_FRAME_NO_SIZE_VIEW | NS_FRAME_NO_MOVE_VIEW | NS_FRAME_NO_VISIBILITY;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GetViewOffset
|
||||
// Retrieves the offset of the given view with the root view, in the
|
||||
// coordinate system of the root view.
|
||||
void
|
||||
nsMenuPopupFrame::GetViewOffset(nsIView* aView, nsPoint& aPoint)
|
||||
{
|
||||
// Notes:
|
||||
// 1) The root view is the client area of the toplevel window that
|
||||
// this popup is anchored to.
|
||||
// 2) Each menupopup is a child of the root view (see
|
||||
// nsMenuPopupFrame::Init())
|
||||
// 3) The coordinates that we return are the total distance between
|
||||
// the top left of the start view and the origin of the root view.
|
||||
|
||||
// Keep track of the root view so that we know to stop there
|
||||
nsIView* rootView;
|
||||
aView->GetViewManager()->GetRootView(rootView);
|
||||
aPoint = aView->GetOffsetTo(rootView);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GetRootViewForPopup
|
||||
// Retrieves the view for the popup widget that contains the given frame.
|
||||
// If the given frame is not contained by a popup widget, return the
|
||||
// root view. This is the root view of the pres context's
|
||||
// viewmanager if aStopAtViewManagerRoot is true; otherwise it's the
|
||||
// root view of the root viewmanager.
|
||||
nsIView*
|
||||
nsMenuPopupFrame::GetRootViewForPopup(nsIFrame* aStartFrame,
|
||||
PRBool aStopAtViewManagerRoot)
|
||||
nsMenuPopupFrame::GetRootViewForPopup(nsIFrame* aStartFrame)
|
||||
{
|
||||
nsIView* view = aStartFrame->GetClosestView();
|
||||
NS_ASSERTION(view, "frame must have a closest view!");
|
||||
if (view) {
|
||||
nsIView* rootView = nsnull;
|
||||
if (aStopAtViewManagerRoot) {
|
||||
view->GetViewManager()->GetRootView(rootView);
|
||||
}
|
||||
|
||||
while (view) {
|
||||
// Walk up the view hierarchy looking for a view whose widget has a
|
||||
// window type of eWindowType_popup - in other words a popup window
|
||||
// widget. If we find one, this is the view we want.
|
||||
nsIWidget* widget = view->GetWidget();
|
||||
if (widget) {
|
||||
nsWindowType wtype;
|
||||
widget->GetWindowType(wtype);
|
||||
if (wtype == eWindowType_popup) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
if (aStopAtViewManagerRoot && view == rootView) {
|
||||
nsIView* rootView = nsnull;
|
||||
while (view) {
|
||||
// Walk up the view hierarchy looking for a view whose widget has a
|
||||
// window type of eWindowType_popup - in other words a popup window
|
||||
// widget. If we find one, this is the view we want.
|
||||
nsIWidget* widget = view->GetWidget();
|
||||
if (widget) {
|
||||
nsWindowType wtype;
|
||||
widget->GetWindowType(wtype);
|
||||
if (wtype == eWindowType_popup) {
|
||||
return view;
|
||||
}
|
||||
|
||||
nsIView* temp = view->GetParent();
|
||||
if (!temp) {
|
||||
// Otherwise, we've walked all the way up to the root view and not
|
||||
// found a view for a popup window widget. Just return the root view.
|
||||
return view;
|
||||
}
|
||||
view = temp;
|
||||
}
|
||||
|
||||
nsIView* temp = view->GetParent();
|
||||
if (!temp) {
|
||||
// Otherwise, we've walked all the way up to the root view and not
|
||||
// found a view for a popup window widget. Just return the root view.
|
||||
return view;
|
||||
}
|
||||
view = temp;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
@ -854,6 +820,7 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
|
||||
PRBool sizedToPopup = PR_FALSE;
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
nsIFrame* rootFrame = presContext->PresShell()->FrameManager()->GetRootFrame();
|
||||
|
||||
// if the frame is not specified, use the anchor node passed to ShowPopup. If
|
||||
// that wasn't specified either, use the root frame. Note that mAnchorContent
|
||||
@ -871,7 +838,7 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
|
||||
}
|
||||
|
||||
if (!aAnchorFrame) {
|
||||
aAnchorFrame = presContext->PresShell()->FrameManager()->GetRootFrame();
|
||||
aAnchorFrame = rootFrame;
|
||||
if (!aAnchorFrame)
|
||||
return NS_OK;
|
||||
}
|
||||
@ -882,96 +849,58 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
|
||||
sizedToPopup = nsMenuFrame::IsSizedToPopup(aAnchorFrame->GetContent(), PR_FALSE);
|
||||
}
|
||||
|
||||
// |containingView|
|
||||
// The view that contains the frame that is invoking this popup. This is
|
||||
// the canvas view inside the scrollport view. It can have negative bounds
|
||||
// if the canvas is scrolled so that part is off screen.
|
||||
nsIView* containingView = nsnull;
|
||||
nsPoint offset;
|
||||
nsMargin margin;
|
||||
containingView = aAnchorFrame->GetClosestView(&offset);
|
||||
if (!containingView)
|
||||
return NS_OK;
|
||||
|
||||
// |parentPos|
|
||||
// The distance between the containingView and the root view. This provides
|
||||
// a hint as to where to position the menu relative to the window.
|
||||
nsPoint parentPos;
|
||||
GetViewOffset(containingView, parentPos);
|
||||
|
||||
// |parentRect|
|
||||
// The dimensions of the frame invoking the popup.
|
||||
nsRect parentRect = aAnchorFrame->GetRect();
|
||||
|
||||
// get the document and the global script object
|
||||
nsIPresShell *presShell = presContext->PresShell();
|
||||
nsIDocument *document = presShell->GetDocument();
|
||||
|
||||
// If we stick to our parent's width, set it here before we move the
|
||||
// window around, because moving is done with respect to the width...
|
||||
if (sizedToPopup) {
|
||||
mRect.width = parentRect.width;
|
||||
}
|
||||
|
||||
// Use containingView instead of parentView, to account for the scrollarrows
|
||||
// that a parent menu might have.
|
||||
nsPoint parentViewWidgetOffset;
|
||||
nsIWidget* parentViewWidget = containingView->GetNearestWidget(&parentViewWidgetOffset);
|
||||
nsRect localParentWidgetRect(0,0,0,0), screenParentWidgetRect;
|
||||
parentViewWidget->WidgetToScreen ( localParentWidgetRect, screenParentWidgetRect );
|
||||
|
||||
// |xpos| and |ypos| hold the x and y positions of where the popup will be moved to,
|
||||
// in _twips_, in the coordinate system of the _parent view_.
|
||||
// in app units, in the coordinate system of the _parent view_.
|
||||
PRBool readjustAboveBelow = PR_FALSE;
|
||||
PRInt32 xpos = 0, ypos = 0;
|
||||
nsMargin margin;
|
||||
|
||||
// the positon in app units where the popup should appear.
|
||||
PRInt32 screenViewLocX, screenViewLocY;
|
||||
|
||||
// the screen rectangle of the anchor, or if null, the root frame, in dev pixels.
|
||||
nsRect anchorScreenRect;
|
||||
nsRect rootScreenRect = rootFrame->GetScreenRect();
|
||||
|
||||
if (mScreenXPos == -1 && mScreenYPos == -1) {
|
||||
// if we are anchored to our parent, there are certain things we don't want to do
|
||||
// when repositioning the view to fit on the screen, such as end up positioned over
|
||||
// the parent. When doing this reposition, we want to move the popup to the side with
|
||||
// the most room. The combination of anchor and alignment dictate if we readjust
|
||||
// above/below or to the left/right.
|
||||
|
||||
if (mAnchorContent) {
|
||||
xpos = parentPos.x + offset.x;
|
||||
ypos = parentPos.y + offset.y;
|
||||
anchorScreenRect = aAnchorFrame->GetScreenRect();
|
||||
xpos = presContext->DevPixelsToAppUnits(anchorScreenRect.x - rootScreenRect.x);
|
||||
ypos = presContext->DevPixelsToAppUnits(anchorScreenRect.y - rootScreenRect.y);
|
||||
|
||||
// move the popup according to the anchor and alignment. This will also tell us
|
||||
// which axis the popup is flush against in case we have to move it around later.
|
||||
AdjustPositionForAnchorAlign(&xpos, &ypos, parentRect, &readjustAboveBelow);
|
||||
|
||||
// the x and y position may be used to offset the popup after it has been anchored
|
||||
xpos += presContext->DevPixelsToAppUnits(mXPos);
|
||||
ypos += presContext->DevPixelsToAppUnits(mYPos);
|
||||
}
|
||||
else {
|
||||
// with no anchor, the popup is positioned relative to the root frame
|
||||
anchorScreenRect = rootScreenRect;
|
||||
GetStyleMargin()->GetMargin(margin);
|
||||
xpos = presContext->DevPixelsToAppUnits(mXPos) + margin.left;
|
||||
ypos = presContext->DevPixelsToAppUnits(mYPos) + margin.top;
|
||||
xpos = margin.left;
|
||||
ypos = margin.top;
|
||||
}
|
||||
|
||||
// Recall that |xpos| and |ypos| are in the coordinate system of the parent view. In
|
||||
// order to determine the screen coordinates of where our view will end up, we
|
||||
// need to find the x/y position of the parent view in screen coords. That is done
|
||||
// by getting the widget associated with the parent view and determining the offset
|
||||
// based on converting (0,0) in its coordinate space to screen coords. We then
|
||||
// offset that point by (|xpos|,|ypos|) to get the true screen coordinates of
|
||||
// the view. *whew*
|
||||
// add on the offset
|
||||
xpos += presContext->CSSPixelsToAppUnits(mXPos);
|
||||
ypos += presContext->CSSPixelsToAppUnits(mYPos);
|
||||
|
||||
// |parentView|
|
||||
// The root view for the window that contains the frame, for frames inside
|
||||
// menupopups this is the first view inside the popup window widget, for
|
||||
// frames inside a toplevel window, this is the root view of the toplevel
|
||||
// window.
|
||||
nsIView* parentView = GetRootViewForPopup(aAnchorFrame, PR_FALSE);
|
||||
if (!parentView)
|
||||
return NS_OK;
|
||||
|
||||
screenViewLocX = presContext->DevPixelsToAppUnits(screenParentWidgetRect.x) +
|
||||
(xpos - parentPos.x) + parentViewWidgetOffset.x;
|
||||
screenViewLocY = presContext->DevPixelsToAppUnits(screenParentWidgetRect.y) +
|
||||
(ypos - parentPos.y) + parentViewWidgetOffset.y;
|
||||
screenViewLocX = presContext->DevPixelsToAppUnits(rootScreenRect.x) + xpos;
|
||||
screenViewLocY = presContext->DevPixelsToAppUnits(rootScreenRect.y) + ypos;
|
||||
}
|
||||
else {
|
||||
// positioned on screen
|
||||
@ -979,19 +908,15 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
|
||||
screenViewLocX = nsPresContext::CSSPixelsToAppUnits(mScreenXPos) + margin.left;
|
||||
screenViewLocY = nsPresContext::CSSPixelsToAppUnits(mScreenYPos) + margin.top;
|
||||
|
||||
xpos = screenViewLocX - presContext->DevPixelsToAppUnits(screenParentWidgetRect.x) -
|
||||
parentViewWidgetOffset.x - parentPos.x;
|
||||
ypos = screenViewLocY - presContext->DevPixelsToAppUnits(screenParentWidgetRect.y) -
|
||||
parentViewWidgetOffset.y - parentPos.y;
|
||||
// determine the x and y position by subtracting the desired screen
|
||||
// position from the screen position of the root frame.
|
||||
xpos = screenViewLocX - presContext->DevPixelsToAppUnits(rootScreenRect.x);
|
||||
ypos = screenViewLocY - presContext->DevPixelsToAppUnits(rootScreenRect.y);
|
||||
}
|
||||
|
||||
// Compute info about the screen dimensions. Because of multiple monitor systems,
|
||||
// the left or top sides of the screen may be in negative space (main monitor is on the
|
||||
// right, etc). We need to be sure to do the right thing.
|
||||
nsPIDOMWindow *window = document->GetWindow();
|
||||
if (!window)
|
||||
return NS_OK;
|
||||
|
||||
nsIDeviceContext* devContext = PresContext()->DeviceContext();
|
||||
nsRect rect;
|
||||
if ( mMenuCanOverlapOSBar ) {
|
||||
@ -1007,7 +932,6 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
|
||||
|
||||
// for content shells, clip to the client area rather than the screen area
|
||||
if (mInContentShell) {
|
||||
nsRect rootScreenRect = presShell->GetRootFrame()->GetScreenRect();
|
||||
rootScreenRect.ScaleRoundIn(presContext->AppUnitsPerDevPixel());
|
||||
rect.IntersectRect(rect, rootScreenRect);
|
||||
}
|
||||
@ -1020,6 +944,8 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
|
||||
PRInt32 screenBottomTwips = rect.YMost();
|
||||
|
||||
if (mPopupAnchor != POPUPALIGNMENT_NONE) {
|
||||
NS_ASSERTION(mScreenXPos == -1 && mScreenYPos == -1,
|
||||
"screen position used with anchor");
|
||||
//
|
||||
// Popup is anchored to the parent, guarantee that it does not cover the parent. We
|
||||
// shouldn't do anything funky if it will already fit on the screen as is.
|
||||
@ -1042,16 +968,6 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
|
||||
// | \/ ||
|
||||
// +------------------------+
|
||||
|
||||
|
||||
|
||||
// compute screen coordinates of parent frame so we can play with it. Make sure we put it
|
||||
// into twips as everything else is as well.
|
||||
nsRect screenParentFrameRect (presContext->AppUnitsToDevPixels(offset.x), presContext->AppUnitsToDevPixels(offset.y),
|
||||
parentRect.width, parentRect.height );
|
||||
parentViewWidget->WidgetToScreen ( screenParentFrameRect, screenParentFrameRect );
|
||||
screenParentFrameRect.x = presContext->DevPixelsToAppUnits(screenParentFrameRect.x);
|
||||
screenParentFrameRect.y = presContext->DevPixelsToAppUnits(screenParentFrameRect.y);
|
||||
|
||||
// Don't let it spill off the screen to the top
|
||||
if (screenViewLocY < screenTopTwips) {
|
||||
PRInt32 moveDist = screenTopTwips - screenViewLocY;
|
||||
@ -1063,7 +979,9 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
|
||||
if ( (screenViewLocX + mRect.width) > screenRightTwips ||
|
||||
screenViewLocX < screenLeftTwips ||
|
||||
(screenViewLocY + mRect.height) > screenBottomTwips ) {
|
||||
|
||||
nsRect screenParentFrameRect(anchorScreenRect);
|
||||
screenParentFrameRect.ScaleRoundOut(PresContext()->AppUnitsPerDevPixel());
|
||||
|
||||
// figure out which side of the parent has the most free space so we can move/resize
|
||||
// the popup there. This should still work if the parent frame is partially screen.
|
||||
PRBool switchSides = IsMoreRoomOnOtherSideOfParent ( readjustAboveBelow, screenViewLocX, screenViewLocY,
|
||||
@ -1576,9 +1494,7 @@ nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doActi
|
||||
NS_IMETHODIMP
|
||||
nsMenuPopupFrame::GetWidget(nsIWidget **aWidget)
|
||||
{
|
||||
// Get parent view
|
||||
// XXX should this be passing PR_FALSE or PR_TRUE for aStopAtViewManagerRoot?
|
||||
nsIView * view = GetRootViewForPopup(this, PR_FALSE);
|
||||
nsIView * view = GetRootViewForPopup(this);
|
||||
if (!view)
|
||||
return NS_OK;
|
||||
|
||||
|
@ -204,9 +204,7 @@ public:
|
||||
// laid out, so that the view can be shown.
|
||||
void AdjustView();
|
||||
|
||||
void GetViewOffset(nsIView* aView, nsPoint& aPoint);
|
||||
nsIView* GetRootViewForPopup(nsIFrame* aStartFrame,
|
||||
PRBool aStopAtViewManagerRoot);
|
||||
nsIView* GetRootViewForPopup(nsIFrame* aStartFrame);
|
||||
|
||||
// set the position of the popup either relative to the anchor aAnchorFrame
|
||||
// (or the frame for mAnchorContent if aAnchorFrame is null) or at a specific
|
||||
|
@ -58,6 +58,9 @@ _TEST_FILES = bug288254_window.xul \
|
||||
window_popup_preventdefault_chrome.xul \
|
||||
test_largemenu.xul \
|
||||
window_largemenu.xul \
|
||||
test_popup_anchor.xul \
|
||||
window_popup_anchor.xul \
|
||||
frame_popup_anchor.xul \
|
||||
$(NULL)
|
||||
|
||||
ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
|
||||
|
44
toolkit/content/tests/chrome/frame_popup_anchor.xul
Normal file
44
toolkit/content/tests/chrome/frame_popup_anchor.xul
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
|
||||
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<menupopup id="popup" onpopupshown="popupShown()">
|
||||
<menuitem label="One"/>
|
||||
<menuitem label="Two"/>
|
||||
</menupopup>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
function openPopup()
|
||||
{
|
||||
document.getElementById("popup").openPopup(parent.document.getElementById("outerbutton"), "after_start", 3, 1);
|
||||
}
|
||||
|
||||
function popupShown()
|
||||
{
|
||||
var popuprect = document.getElementById("popup").getBoundingClientRect();
|
||||
var iframerect = parent.document.getElementById("frame").getBoundingClientRect();
|
||||
var buttonrect = parent.document.getElementById("outerbutton").getBoundingClientRect();
|
||||
|
||||
// The popup should appear anchored on the bottom left edge of the button, however
|
||||
// the client rectangle is relative to the iframe's document. Thus the coordinates
|
||||
// are:
|
||||
// left = iframe's left - anchor button's left - 3 pixel offset passed to openPopup +
|
||||
// iframe border (17px) + iframe padding (0)
|
||||
// top = iframe's top - anchor button's bottom - 1 pixel offset passed to openPopup +
|
||||
// iframe border (0) + iframe padding (3px);
|
||||
var left = -(Math.round(iframerect.left) - Math.round(buttonrect.left) + 14);
|
||||
var top = -(Math.round(iframerect.top) - Math.round(buttonrect.bottom) + 2);
|
||||
|
||||
parent.opener.wrappedJSObject.SimpleTest.is(Math.round(popuprect.left), left, "popup left");
|
||||
parent.opener.wrappedJSObject.SimpleTest.is(Math.round(popuprect.top), top, "popup top");
|
||||
parent.opener.wrappedJSObject.SimpleTest.finish();
|
||||
parent.close();
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
</page>
|
32
toolkit/content/tests/chrome/test_popup_anchor.xul
Normal file
32
toolkit/content/tests/chrome/test_popup_anchor.xul
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Popup Anchor Tests"
|
||||
onload="setTimeout(runTest, 0);"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
function runTest()
|
||||
{
|
||||
window.open("window_popup_anchor.xul", "_new", "chrome,width=600,height=600");
|
||||
}
|
||||
</script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display">
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
</window>
|
27
toolkit/content/tests/chrome/window_popup_anchor.xul
Normal file
27
toolkit/content/tests/chrome/window_popup_anchor.xul
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
|
||||
<window title="Popup Anchor Tests"
|
||||
onload="setTimeout(runTests, 0)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script>
|
||||
function runTests()
|
||||
{
|
||||
frames[0].openPopup();
|
||||
}
|
||||
</script>
|
||||
|
||||
<spacer height="13"/>
|
||||
<button id="outerbutton" label="Button One" style="margin-left: 6px; -moz-appearance: none;"/>
|
||||
<hbox>
|
||||
<spacer width="20"/>
|
||||
<deck>
|
||||
<vbox>
|
||||
<iframe id="frame" style="margin-left: 60px; margin-top: 10px; border-left: 17px solid red; padding-left: 0 !important; padding-top: 3px;"
|
||||
width="250" height="80" src="frame_popup_anchor.xul"/>
|
||||
</vbox>
|
||||
</deck>
|
||||
</hbox>
|
||||
|
||||
</window>
|
Loading…
Reference in New Issue
Block a user