Bug 513162 - Widget additions for recycling top level widgets as content containers. r=dbaron.

This commit is contained in:
Jim Mathies 2010-06-24 21:01:06 -05:00
Родитель b6fd12e643
Коммит 8c631afae5
4 изменённых файлов: 184 добавлений и 35 удалений

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

@ -63,8 +63,8 @@ enum nsViewVisibility {
// IID for the nsIView interface
#define NS_IVIEW_IID \
{ 0xe981334b, 0x756e, 0x417a, \
{ 0xbf, 0x18, 0x47, 0x4a, 0x2d, 0xfe, 0xc3, 0x87 } }
{ 0xdb512cfa, 0xe00c, 0x4eff, \
{ 0xa2, 0x9c, 0x18, 0x74, 0x96, 0x63, 0x17, 0x69 } }
// Public view flags are defined in this file
#define NS_VIEW_FLAGS_PUBLIC 0x00FF
@ -291,6 +291,26 @@ public:
nsContentType aWindowType = eContentTypeInherit,
nsIWidget* aParentWidget = nsnull);
/**
* Attach/detach a top level widget from this view. When attached, the view
* updates the widget's device context and allows the view to begin receiving
* gecko events. The underlying base window associated with the widget will
* continues to receive events it expects.
*
* An attached widget will not be destroyed when the view is destroyed,
* allowing the recycling of a single top level widget over multiple views.
*
* @param aWidget The widget to attach to / detach from.
*/
nsresult AttachToTopLevelWidget(nsIWidget* aWidget);
nsresult DetachFromTopLevelWidget();
/**
* Returns a flag indicating whether the view owns it's widget
* or is attached to an existing top level widget.
*/
PRBool IsAttachedToTopLevel() const { return mWidgetIsTopLevel; }
/**
* In 4.0, the "cutout" nature of a view is queryable.
* If we believe that all cutout view have a native widget, this
@ -373,6 +393,7 @@ protected:
float mOpacity;
PRUint32 mVFlags;
nsWeakView* mDeletionObserver;
PRBool mWidgetIsTopLevel;
virtual ~nsIView() {}
};

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

@ -144,15 +144,47 @@ static ViewWrapper* GetWrapperFor(nsIWidget* aWidget)
return nsnull;
}
//
// Main events handler
//
nsEventStatus HandleEvent(nsGUIEvent *aEvent)
{
//printf(" %d %d %d (%d,%d) \n", aEvent->widget, aEvent->widgetSupports,
// aEvent->message, aEvent->point.x, aEvent->point.y);
static nsEventStatus HandleEvent(nsGUIEvent *aEvent)
{
#if 0
printf(" %d %d %d (%d,%d) \n", aEvent->widget, aEvent->widgetSupports,
aEvent->message, aEvent->point.x, aEvent->point.y);
#endif
nsEventStatus result = nsEventStatus_eIgnore;
nsView *view = nsView::GetViewFor(aEvent->widget);
nsView *view = nsView::GetViewFor(aEvent->widget);
if (view)
{
nsCOMPtr<nsIViewManager> vm = view->GetViewManager();
vm->DispatchEvent(aEvent, view, &result);
}
return result;
}
// Attached widget event helpers
static ViewWrapper* GetAttachedWrapperFor(nsIWidget* aWidget)
{
NS_PRECONDITION(nsnull != aWidget, "null widget ptr");
return aWidget->GetAttachedViewPtr();
}
static nsView* GetAttachedViewFor(nsIWidget* aWidget)
{
NS_PRECONDITION(nsnull != aWidget, "null widget ptr");
ViewWrapper* wrapper = GetAttachedWrapperFor(aWidget);
if (!wrapper)
return nsnull;
return wrapper->GetView();
}
// event handler
static nsEventStatus AttachedHandleEvent(nsGUIEvent *aEvent)
{
nsEventStatus result = nsEventStatus_eIgnore;
nsView *view = GetAttachedViewFor(aEvent->widget);
if (view)
{
@ -176,6 +208,7 @@ nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility)
mViewManager = aViewManager;
mDirtyRegion = nsnull;
mDeletionObserver = nsnull;
mWidgetIsTopLevel = PR_FALSE;
}
void nsView::DropMouseGrabbing()
@ -240,8 +273,21 @@ nsView::~nsView()
ViewWrapper* wrapper = GetWrapperFor(mWindow);
NS_IF_RELEASE(wrapper);
mWindow->SetClientData(nsnull);
mWindow->Destroy();
// If we are not attached to a base window, we're going to tear down our
// widget here. However, if we're attached to somebody elses widget, we
// want to leave the widget alone: don't reset the client data or call
// Destroy. Just clear our event view ptr and free our reference to it.
if (mWidgetIsTopLevel) {
ViewWrapper* wrapper = GetAttachedWrapperFor(mWindow);
NS_IF_RELEASE(wrapper);
mWindow->SetAttachedViewPtr(nsnull);
}
else {
mWindow->SetClientData(nsnull);
mWindow->Destroy();
}
NS_RELEASE(mWindow);
}
delete mDirtyRegion;
@ -275,8 +321,13 @@ nsIView* nsIView::GetViewFor(nsIWidget* aWidget)
NS_PRECONDITION(nsnull != aWidget, "null widget ptr");
ViewWrapper* wrapper = GetWrapperFor(aWidget);
if (!wrapper)
wrapper = GetAttachedWrapperFor(aWidget);
if (wrapper)
return wrapper->GetView();
return wrapper->GetView();
return nsnull;
}
@ -354,7 +405,7 @@ nsIntRect nsIView::CalcWidgetBounds(nsWindowType aType)
if (parentWidget && aType == eWindowType_popup &&
IsEffectivelyVisible()) {
// put offset into screen coordinates
// put offset into screen coordinates. (based on client area origin)
nsIntPoint screenPoint = parentWidget->WidgetToScreenOffset();
viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a),
NSIntPixelsToAppUnits(screenPoint.y, p2a));
@ -390,6 +441,7 @@ void nsView::DoResetWidgetBounds(PRBool aMoveOnly,
nsIntRect curBounds;
mWindow->GetBounds(curBounds);
nsWindowType type;
mWindow->GetWindowType(type);
@ -409,6 +461,7 @@ void nsView::DoResetWidgetBounds(PRBool aMoveOnly,
PRBool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
PRBool changedSize = curBounds.Size() != newBounds.Size();
// Child views are never attached to top level widgets, this is safe.
if (changedPos) {
if (changedSize && !aMoveOnly) {
mWindow->Resize(newBounds.x, newBounds.y, newBounds.width, newBounds.height,
@ -704,6 +757,62 @@ nsresult nsIView::CreateWidget(const nsIID &aWindowIID,
return NS_OK;
}
// Attach to a top level widget and start receiving mirrored events.
nsresult nsIView::AttachToTopLevelWidget(nsIWidget* aWidget)
{
NS_PRECONDITION(nsnull != aWidget, "null widget ptr");
/// XXXjimm This is a temporary workaround to an issue w/document
// viewer (bug 513162).
nsIView *oldView = GetAttachedViewFor(aWidget);
if (oldView) {
oldView->DetachFromTopLevelWidget();
}
nsCOMPtr<nsIDeviceContext> dx;
mViewManager->GetDeviceContext(*getter_AddRefs(dx));
// Note, the previous device context will be released. Detaching
// will not restore the old one.
nsresult rv = aWidget->AttachViewToTopLevel(::AttachedHandleEvent, dx);
if (NS_FAILED(rv))
return rv;
mWindow = aWidget;
NS_ADDREF(mWindow);
nsView* v = static_cast<nsView*>(this);
ViewWrapper* wrapper = new ViewWrapper(v);
NS_ADDREF(wrapper);
mWindow->SetAttachedViewPtr(wrapper);
mWindow->EnableDragDrop(PR_TRUE);
mWidgetIsTopLevel = PR_TRUE;
// Refresh the view bounds
nsWindowType type;
mWindow->GetWindowType(type);
CalcWidgetBounds(type);
return NS_OK;
}
// Detach this view from an attached widget.
nsresult nsIView::DetachFromTopLevelWidget()
{
NS_PRECONDITION(mWidgetIsTopLevel, "Not attached currently!");
NS_PRECONDITION(mWindow, "null mWindow for DetachFromTopLevelWidget!");
// Release memory for the view wrapper
ViewWrapper* wrapper = GetAttachedWrapperFor(mWindow);
NS_IF_RELEASE(wrapper);
mWindow->SetAttachedViewPtr(nsnull);
NS_RELEASE(mWindow);
mWidgetIsTopLevel = PR_FALSE;
return NS_OK;
}
void nsView::SetZIndex(PRBool aAuto, PRInt32 aZIndex, PRBool aTopMost)
{
PRBool oldIsAuto = GetZIndexIsAuto();
@ -834,6 +943,8 @@ nsIntPoint nsIView::GetScreenPosition() const
PRInt32 p2a = dx->AppUnitsPerDevPixel();
nsIntPoint ourPoint(NSAppUnitsToIntPixels(toWidgetOffset.x, p2a),
NSAppUnitsToIntPixels(toWidgetOffset.y, p2a));
// WidgetToScreenOffset is at the origin of the client area of
// the widget.
screenPoint = ourPoint + widget->WidgetToScreenOffset();
}
@ -842,6 +953,9 @@ nsIntPoint nsIView::GetScreenPosition() const
nsIWidget* nsIView::GetNearestWidget(nsPoint* aOffset) const
{
// aOffset is based on the view's position, which ignores any chrome on
// attached parent widgets.
nsPoint pt(0, 0);
const nsView* v;
for (v = static_cast<const nsView*>(this);
@ -855,9 +969,9 @@ nsIWidget* nsIView::GetNearestWidget(nsPoint* aOffset) const
return nsnull;
}
// pt is now the offset from v's origin to this's origin
// The widget's origin is the top left corner of v's bounds, which may
// not coincide with v's origin
// pt is now the offset from v's origin to this view's origin. The widget's
// origin is the top left corner of v's bounds, which may not coincide with
// the view's origin.
if (aOffset) {
nsRect vBounds = v->GetBounds();
*aOffset = pt + v->GetPosition() - nsPoint(vBounds.x, vBounds.y) +

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

@ -304,6 +304,20 @@ NS_IMETHODIMP nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeig
return NS_OK;
}
void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
{
nsRect oldDim;
nsRect newDim(0, 0, aWidth, aHeight);
mRootView->GetDimensions(oldDim);
// We care about resizes even when one dimension is already zero.
if (!oldDim.IsExactEqual(newDim)) {
// Don't resize the widget. It is already being set elsewhere.
mRootView->SetDimensions(newDim, PR_TRUE, PR_FALSE);
if (mObserver)
mObserver->ResizeReflow(mRootView, aWidth, aHeight);
}
}
NS_IMETHODIMP nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
{
if (mRootView) {
@ -578,6 +592,13 @@ nsViewManager::UpdateWidgetArea(nsView *aWidgetView, nsIWidget* aWidget,
const nsRegion &aDamagedRegion,
nsView* aIgnoreWidgetView)
{
#if 0
nsRect dbgBounds = aDamagedRegion.GetBounds();
printf("UpdateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
aWidgetView, aWidgetView->IsAttachedToTopLevel(),
aWidget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
#endif
if (!IsRefreshEnabled()) {
// accumulate this rectangle in the view's dirty region, so we can
// process it later.
@ -639,14 +660,19 @@ nsViewManager::UpdateWidgetArea(nsView *aWidgetView, nsIWidget* aWidget,
// Don't mess with views that are in completely different view
// manager trees
if (view->GetViewManager()->RootViewManager() == RootViewManager()) {
// get the damage region into 'view's coordinate system
// get the damage region into view's coordinate system
nsRegion damage = intersection;
nsPoint offset = view->GetOffsetTo(aWidgetView);
damage.MoveBy(-offset);
// Update the child and it's children
UpdateWidgetArea(view, childWidget, damage, aIgnoreWidgetView);
// GetBounds should compensate for chrome on a toplevel widget
nsIntRect bounds;
childWidget->GetBounds(bounds);
nsTArray<nsIntRect> clipRects;
childWidget->GetWindowClipRegion(&clipRects);
for (PRUint32 i = 0; i < clipRects.Length(); ++i) {
@ -741,10 +767,9 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
{
if (aView)
{
// client area dimensions are set on the view
nscoord width = ((nsSizeEvent*)aEvent)->windowSize->width;
nscoord height = ((nsSizeEvent*)aEvent)->windowSize->height;
width = ((nsSizeEvent*)aEvent)->mWinWidth;
height = ((nsSizeEvent*)aEvent)->mWinHeight;
// The root view may not be set if this is the resize associated with
// window creation
@ -1011,9 +1036,10 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsGUIEvent* aEvent)
{
//printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports,
// event->message, event->point.x, event->point.y);
#if 0
printf(" %d %d %d %d (%d,%d) \n", this, event->widget, event->widgetSupports,
event->message, event->point.x, event->point.y);
#endif
// Hold a refcount to the observer. The continued existence of the observer will
// delay deletion of this view hierarchy should the event want to cause its
// destruction in, say, some JavaScript event handler.

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

@ -209,19 +209,7 @@ private:
*/
nsIntRect ViewToWidget(nsView *aView, nsView* aWidgetView, const nsRect &aRect) const;
void DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
{
nsRect oldDim;
nsRect newDim(0, 0, aWidth, aHeight);
mRootView->GetDimensions(oldDim);
// We care about resizes even when one dimension is already zero.
if (!oldDim.IsExactEqual(newDim)) {
// Don't resize the widget. It is already being set elsewhere.
mRootView->SetDimensions(newDim, PR_TRUE, PR_FALSE);
if (mObserver)
mObserver->ResizeReflow(mRootView, aWidth, aHeight);
}
}
void DoSetWindowDimensions(nscoord aWidth, nscoord aHeight);
// Safety helpers
void IncrementUpdateCount() {