From 9e94a3b52120276ce66dc21af66ff2c5b96a2971 Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Tue, 23 Nov 2004 17:26:19 +0000 Subject: [PATCH] Add methods to get screen coordinates of frames and views. Bug 268576, r+sr=roc --- accessible/src/atk/nsAccessibleText.cpp | 47 +++------------ content/svg/content/src/nsSVGSVGElement.cpp | 65 +++------------------ layout/base/public/nsIFrame.h | 7 +++ layout/generic/nsFrame.cpp | 31 ++++++++++ layout/generic/nsIFrame.h | 7 +++ layout/html/base/src/nsFrame.cpp | 31 ++++++++++ layout/xul/base/src/nsBoxObject.cpp | 57 +++++------------- layout/xul/base/src/nsBoxObject.h | 3 +- layout/xul/base/src/nsMenuPopupFrame.cpp | 35 +---------- layout/xul/base/src/nsMenuPopupFrame.h | 1 - view/public/nsIView.h | 9 ++- view/src/nsView.cpp | 27 +++++++-- 12 files changed, 141 insertions(+), 179 deletions(-) diff --git a/accessible/src/atk/nsAccessibleText.cpp b/accessible/src/atk/nsAccessibleText.cpp index ecf0e88844cd..44bfed08a93f 100644 --- a/accessible/src/atk/nsAccessibleText.cpp +++ b/accessible/src/atk/nsAccessibleText.cpp @@ -712,7 +712,7 @@ NS_IMETHODIMP nsAccessibleText::GetCharacterExtents(PRInt32 aOffset, shell->GetPrimaryFrameFor(content, &frame); NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); - nsRect frameRect = frame->GetRect(); + nsIntRect frameScreenRect = frame->GetScreenRectExternal(); nsCOMPtr rc; shell->CreateRenderingContext(frame, getter_AddRefs(rc)); @@ -749,43 +749,15 @@ NS_IMETHODIMP nsAccessibleText::GetCharacterExtents(PRInt32 aOffset, *aHeight = NSTwipsToIntPixels(tmpHeight, t2p); } - //Getting x and y - PRInt32 tmpX, tmpY; - tmpX = frameRect.x; - tmpY = frameRect.y; - //add the width of the string before current char nsAutoString beforeString; nscoord beforeWidth; if (NS_SUCCEEDED(GetText(0, aOffset, beforeString)) && NS_SUCCEEDED(rc->GetWidth(beforeString, beforeWidth))) { - tmpX += beforeWidth; - } - - //find the topest frame, add the offset recursively - nsIFrame* tmpFrame = frame; - nsIFrame* parentFrame = tmpFrame->GetParent(); - while (parentFrame) { - nsPoint origin = parentFrame->GetPosition(); - tmpX += origin.x; - tmpY += origin.y; - tmpFrame = parentFrame; - parentFrame = tmpFrame->GetParent(); - } - - tmpX = NSTwipsToIntPixels(tmpX, t2p); - tmpY = NSTwipsToIntPixels(tmpY, t2p); - - //change to screen co-ord - nsIWidget *frameWidget = tmpFrame->GetWindow(); - if (frameWidget) { - nsRect oldRect(tmpX, tmpY, 0, 0), newRect; - if (NS_SUCCEEDED(frameWidget->WidgetToScreen(oldRect, newRect))) { - tmpX = newRect.x; - tmpY = newRect.y; - } + frameScreenRect.x += NSTwipsToIntPixels(beforeWidth, t2p); } + PRInt32 screenX = 0, screenY = 0; if (aCoordType == COORD_TYPE_WINDOW) { //co-ord type = window nsCOMPtr docView(do_QueryInterface(doc)); @@ -798,20 +770,15 @@ NS_IMETHODIMP nsAccessibleText::GetCharacterExtents(PRInt32 aOffset, nsCOMPtr windowInter(do_QueryInterface(abstractView)); NS_ENSURE_TRUE(windowInter, NS_ERROR_FAILURE); - PRInt32 screenX,screenY; if (NS_FAILED(windowInter->GetScreenX(&screenX)) || NS_FAILED(windowInter->GetScreenY(&screenY))) { return NS_ERROR_FAILURE; } + } + // else default: co-ord type = screen - *aX = tmpX - screenX; - *aY = tmpY - screenY; - } - else { - //default: co-ord type = screen - *aX = tmpX; - *aY = tmpY; - } + *aX = frameScreenRect.x - screenX; + *aY = frameScreenRect.y - screenY; return NS_OK; } diff --git a/content/svg/content/src/nsSVGSVGElement.cpp b/content/svg/content/src/nsSVGSVGElement.cpp index 58734c708812..3ced4f7e68f9 100644 --- a/content/svg/content/src/nsSVGSVGElement.cpp +++ b/content/svg/content/src/nsSVGSVGElement.cpp @@ -1200,69 +1200,22 @@ void nsSVGSVGElement::GetScreenPosition(PRInt32 &x, PRInt32 &y) nsIDocument *document = GetCurrentDoc(); if (!document) return; + // Flush all pending notifications so that our frames are uptodate + // Make sure to do this before we start grabbing layout objects like + // presshells. + document->FlushPendingNotifications(Flush_Layout); + nsIPresShell *presShell = document->GetShellAt(0); if (!presShell) { - NS_ERROR("couldn't get presshell"); return; } - nsPresContext *context = presShell->GetPresContext(); - if (!context) { - NS_ERROR("couldn't get prescontext"); - return; - } - - // Flush all pending notifications so that our frames are uptodate - document->FlushPendingNotifications(Flush_Layout); - nsIFrame* frame; presShell->GetPrimaryFrameFor(this, &frame); - float t2p; - t2p = context->TwipsToPixels(); - - - nsIWidget* widget = nsnull; - - while (frame) { - // Look for a widget so we can get screen coordinates - nsIView* view = frame->GetView(); - if (view) { - // handle scrolled views along the way: - nsIScrollableView* scrollableView = nsnull; - CallQueryInterface(view, &scrollableView); - if (scrollableView) { - nscoord scrollX, scrollY; - scrollableView->GetScrollPosition(scrollX, scrollY); - x -= scrollX; - y -= scrollY; - } - - // if this is a widget we break and get screen coords from it: - widget = view->GetWidget(); - if (widget) - break; - } - - // No widget yet, so count up the coordinates of the frame - nsPoint origin = frame->GetPosition(); - x += origin.x; - y += origin.y; - - frame = frame->GetParent(); - } - - - // Convert to pixels using that scale - x = NSTwipsToIntPixels(x, t2p); - y = NSTwipsToIntPixels(y, t2p); - - if (widget) { - // Add the widget's screen coordinates to the offset we've counted - nsRect client(0,0,0,0); - nsRect screen; - widget->WidgetToScreen(client, screen); - x += screen.x; - y += screen.y; + if (frame) { + nsIntRect rect = frame->GetScreenRect(); + x = rect.x; + y = rect.y; } } diff --git a/layout/base/public/nsIFrame.h b/layout/base/public/nsIFrame.h index 5e97e631b932..053fc56f612a 100644 --- a/layout/base/public/nsIFrame.h +++ b/layout/base/public/nsIFrame.h @@ -999,6 +999,13 @@ public: nsPoint GetOffsetTo(const nsIFrame* aOther) const; virtual nsPoint GetOffsetToExternal(const nsIFrame* aOther) const; + /** + * Get the screen rect of the frame. + * @return the pixel rect of the frame in screen coordinates. + */ + nsIntRect GetScreenRect() const; + virtual nsIntRect GetScreenRectExternal() const; + /** * Returns the offset from this frame to the closest geometric parent that * has a view. Also returns the containing view or null in case of error diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 8cd948f30ceb..c9b95e54c8cc 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2296,6 +2296,37 @@ nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const return offset; } +// virtual +nsIntRect nsIFrame::GetScreenRectExternal() const +{ + return GetScreenRect(); +} + +nsIntRect nsIFrame::GetScreenRect() const +{ + nsIntRect retval(0,0,0,0); + nsPoint toViewOffset(0,0); + nsIView* view = GetClosestView(&toViewOffset); + + if (view) { + nsPoint toWidgetOffset(0,0); + nsIWidget* widget = view->GetNearestWidget(&toWidgetOffset); + + if (widget) { + nsRect ourRect = mRect; + ourRect += toViewOffset + toWidgetOffset; + ourRect.ScaleRoundOut(GetPresContext()->TwipsToPixels()); + // Is it safe to pass the same rect for both args of WidgetToScreen? + // It's not clear, so let's not... + nsIntRect ourPxRect(ourRect.x, ourRect.y, ourRect.width, ourRect.height); + + widget->WidgetToScreen(ourPxRect, retval); + } + } + + return retval; +} + // Returns the offset from this frame to the closest geometric parent that // has a view. Also returns the containing view or null in case of error NS_IMETHODIMP nsFrame::GetOffsetFromView(nsPresContext* aPresContext, diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 5e97e631b932..053fc56f612a 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -999,6 +999,13 @@ public: nsPoint GetOffsetTo(const nsIFrame* aOther) const; virtual nsPoint GetOffsetToExternal(const nsIFrame* aOther) const; + /** + * Get the screen rect of the frame. + * @return the pixel rect of the frame in screen coordinates. + */ + nsIntRect GetScreenRect() const; + virtual nsIntRect GetScreenRectExternal() const; + /** * Returns the offset from this frame to the closest geometric parent that * has a view. Also returns the containing view or null in case of error diff --git a/layout/html/base/src/nsFrame.cpp b/layout/html/base/src/nsFrame.cpp index 8cd948f30ceb..c9b95e54c8cc 100644 --- a/layout/html/base/src/nsFrame.cpp +++ b/layout/html/base/src/nsFrame.cpp @@ -2296,6 +2296,37 @@ nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const return offset; } +// virtual +nsIntRect nsIFrame::GetScreenRectExternal() const +{ + return GetScreenRect(); +} + +nsIntRect nsIFrame::GetScreenRect() const +{ + nsIntRect retval(0,0,0,0); + nsPoint toViewOffset(0,0); + nsIView* view = GetClosestView(&toViewOffset); + + if (view) { + nsPoint toWidgetOffset(0,0); + nsIWidget* widget = view->GetNearestWidget(&toWidgetOffset); + + if (widget) { + nsRect ourRect = mRect; + ourRect += toViewOffset + toWidgetOffset; + ourRect.ScaleRoundOut(GetPresContext()->TwipsToPixels()); + // Is it safe to pass the same rect for both args of WidgetToScreen? + // It's not clear, so let's not... + nsIntRect ourPxRect(ourRect.x, ourRect.y, ourRect.width, ourRect.height); + + widget->WidgetToScreen(ourPxRect, retval); + } + } + + return retval; +} + // Returns the offset from this frame to the closest geometric parent that // has a view. Also returns the containing view or null in case of error NS_IMETHODIMP nsFrame::GetOffsetFromView(nsPresContext* aPresContext, diff --git a/layout/xul/base/src/nsBoxObject.cpp b/layout/xul/base/src/nsBoxObject.cpp index c236c0e0a61f..b7d6f1fd3c7c 100644 --- a/layout/xul/base/src/nsBoxObject.cpp +++ b/layout/xul/base/src/nsBoxObject.cpp @@ -264,11 +264,10 @@ nsBoxObject::GetOffsetRect(nsRect& aRect) } nsresult -nsBoxObject::GetScreenRect(nsRect& aRect) +nsBoxObject::GetScreenPosition(nsIntPoint& aPoint) { - aRect.x = aRect.y = 0; - aRect.Empty(); - + aPoint.x = aPoint.y = 0; + if (!mContent) return NS_ERROR_NOT_INITIALIZED; @@ -287,40 +286,10 @@ nsBoxObject::GetScreenRect(nsRect& aRect) nsIFrame* frame; presShell->GetPrimaryFrameFor(mContent, &frame); - PRInt32 offsetX = 0; - PRInt32 offsetY = 0; - nsIWidget* widget = nsnull; - - while (frame) { - // Look for a widget so we can get screen coordinates - if (frame->HasView()) { - widget = frame->GetView()->GetWidget(); - if (widget) - break; - } - - // No widget yet, so count up the coordinates of the frame - nsPoint origin = frame->GetPosition(); - offsetX += origin.x; - offsetY += origin.y; - - frame = frame->GetParent(); - } - - if (widget) { - // Get the scale from that Presentation Context - float scale; - scale = presContext->TwipsToPixels(); - - // Convert to pixels using that scale - offsetX = NSTwipsToIntPixels(offsetX, scale); - offsetY = NSTwipsToIntPixels(offsetY, scale); - - // Add the widget's screen coordinates to the offset we've counted - nsRect oldBox(0,0,0,0); - widget->WidgetToScreen(oldBox, aRect); - aRect.x += offsetX; - aRect.y += offsetY; + if (frame) { + nsIntRect rect = frame->GetScreenRect(); + aPoint.x = rect.x; + aPoint.y = rect.y; } } } @@ -368,11 +337,11 @@ nsBoxObject::GetHeight(PRInt32* aResult) NS_IMETHODIMP nsBoxObject::GetScreenX(PRInt32 *_retval) { - nsRect rect; - nsresult rv = GetScreenRect(rect); + nsIntPoint position; + nsresult rv = GetScreenPosition(position); if (NS_FAILED(rv)) return rv; - *_retval = rect.x; + *_retval = position.x; return NS_OK; } @@ -380,11 +349,11 @@ nsBoxObject::GetScreenX(PRInt32 *_retval) NS_IMETHODIMP nsBoxObject::GetScreenY(PRInt32 *_retval) { - nsRect rect; - nsresult rv = GetScreenRect(rect); + nsIntPoint position; + nsresult rv = GetScreenPosition(position); if (NS_FAILED(rv)) return rv; - *_retval = rect.y; + *_retval = position.y; return NS_OK; } diff --git a/layout/xul/base/src/nsBoxObject.h b/layout/xul/base/src/nsBoxObject.h index f079a346a3c3..5010984d97ad 100644 --- a/layout/xul/base/src/nsBoxObject.h +++ b/layout/xul/base/src/nsBoxObject.h @@ -39,6 +39,7 @@ #include "nsIBoxObject.h" #include "nsPIBoxObject.h" #include "nsIPresState.h" +#include "nsPoint.h" class nsIBoxLayoutManager; class nsIBoxPaintManager; @@ -62,7 +63,7 @@ public: virtual nsIFrame* GetFrame(); nsresult GetOffsetRect(nsRect& aRect); - nsresult GetScreenRect(nsRect& aRect); + nsresult GetScreenPosition(nsIntPoint& aPoint); nsIDOMElement* GetChildByOrdinalAt(PRUint32 aIndex); protected: diff --git a/layout/xul/base/src/nsMenuPopupFrame.cpp b/layout/xul/base/src/nsMenuPopupFrame.cpp index c07bfd3d3959..3c1a93d2113d 100644 --- a/layout/xul/base/src/nsMenuPopupFrame.cpp +++ b/layout/xul/base/src/nsMenuPopupFrame.cpp @@ -2204,45 +2204,16 @@ nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop) mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::top, top, PR_FALSE); nsIView* view = GetView(); + NS_ASSERTION(view->GetParent(), "Must have parent!"); // Retrieve screen position of parent view - nsPoint screenPos; - GetScreenPosition(view->GetParent(), screenPos); + nsIntPoint screenPos = view->GetParent()->GetScreenPosition(); // Move the widget + // XXXbz don't we want screenPos to be the parent _widget_'s position, then? view->GetWidget()->Move(aLeft - screenPos.x, aTop - screenPos.y); } -void -nsMenuPopupFrame::GetScreenPosition(nsIView* aView, nsPoint& aScreenPosition) -{ - nsPoint screenPos(0,0); - - nsIView* currView = aView; - nsIView* nextView = nsnull; - - while (1) { - screenPos += currView->GetPosition(); - - nextView = currView->GetParent(); - if (!nextView) - break; - else - currView = nextView; - } - - nsIWidget* rootWidget = currView->GetWidget(); - nsRect bounds, screenBounds; - rootWidget->GetScreenBounds(bounds); - rootWidget->WidgetToScreen(bounds, screenBounds); - - float t2p; - t2p = mPresContext->TwipsToPixels(); - - aScreenPosition.x = NSTwipsToIntPixels(screenPos.x, t2p) + screenBounds.x; - aScreenPosition.y = NSTwipsToIntPixels(screenPos.y, t2p) + screenBounds.y; -} - void nsMenuPopupFrame::GetAutoPosition(PRBool* aShouldAutoPosition) { diff --git a/layout/xul/base/src/nsMenuPopupFrame.h b/layout/xul/base/src/nsMenuPopupFrame.h index 34e24ca428de..c8500635bea9 100644 --- a/layout/xul/base/src/nsMenuPopupFrame.h +++ b/layout/xul/base/src/nsMenuPopupFrame.h @@ -178,7 +178,6 @@ public: void EnsureMenuItemIsVisible(nsIMenuFrame* aMenuFrame); void MoveTo(PRInt32 aLeft, PRInt32 aTop); - void GetScreenPosition(nsIView* aView, nsPoint& aScreenPosition); void GetAutoPosition(PRBool* aShouldAutoPosition); void SetAutoPosition(PRBool aShouldAutoPosition); diff --git a/view/public/nsIView.h b/view/public/nsIView.h index 0bc9b9973cfd..9b2b4cd726b0 100644 --- a/view/public/nsIView.h +++ b/view/public/nsIView.h @@ -185,6 +185,13 @@ public: */ nsPoint GetOffsetTo(const nsIView* aOther) const; + /** + * Get the screen position of the view. + * @return the pixel position of the top-left of the view in screen + * coordinates. + */ + nsIntPoint GetScreenPosition() const; + /** * Called to query the visibility state of a view. * @result current visibility state @@ -291,7 +298,7 @@ public: * XXX Remove this 'virtual' when gfx+widget are merged into gklayout; * Mac widget depends on this method, which is BOGUS! */ - virtual nsIWidget* GetNearestWidget(nsPoint* aOffset); + virtual nsIWidget* GetNearestWidget(nsPoint* aOffset) const; /** * Create a widget to associate with this view. diff --git a/view/src/nsView.cpp b/view/src/nsView.cpp index 0b0a8ee0a623..6f85ff2c5eab 100644 --- a/view/src/nsView.cpp +++ b/view/src/nsView.cpp @@ -754,11 +754,30 @@ nsPoint nsIView::GetOffsetTo(const nsIView* aOther) const return offset; } -nsIWidget* nsIView::GetNearestWidget(nsPoint* aOffset) +nsIntPoint nsIView::GetScreenPosition() const +{ + nsIntRect screenRect(0,0,0,0); + nsPoint toWidgetOffset(0,0); + nsIWidget* widget = GetNearestWidget(&toWidgetOffset); + if (widget) { + nsCOMPtr dx; + mViewManager->GetDeviceContext(*getter_AddRefs(dx)); + float t2p = dx->AppUnitsToDevUnits(); + nsIntRect ourRect(NSTwipsToIntPixels(toWidgetOffset.x, t2p), + NSTwipsToIntPixels(toWidgetOffset.y, t2p), + 0, + 0); + widget->WidgetToScreen(ourRect, screenRect); + } + + return nsIntPoint(screenRect.x, screenRect.y); +} + +nsIWidget* nsIView::GetNearestWidget(nsPoint* aOffset) const { nsPoint pt(0, 0); - nsView* v; - for (v = NS_STATIC_CAST(nsView*, this); + const nsView* v; + for (v = NS_STATIC_CAST(const nsView*, this); v && !v->HasWidget(); v = v->GetParent()) { pt += v->GetPosition(); } @@ -766,7 +785,7 @@ nsIWidget* nsIView::GetNearestWidget(nsPoint* aOffset) if (aOffset) { *aOffset = pt; } - return NS_STATIC_CAST(nsView*, this)->GetViewManager()->GetWidget(); + return NS_STATIC_CAST(const nsView*, this)->GetViewManager()->GetWidget(); } // pt is now the offset from v's origin to this's origin