michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsViewManager_h___ michael@0: #define nsViewManager_h___ michael@0: michael@0: #include "nscore.h" michael@0: #include "nsView.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsCRT.h" michael@0: #include "nsVoidArray.h" michael@0: #include "nsDeviceContext.h" michael@0: #include "nsTArray.h" michael@0: #include "mozilla/EventForwards.h" michael@0: michael@0: class nsIWidget; michael@0: struct nsRect; michael@0: class nsRegion; michael@0: class nsDeviceContext; michael@0: class nsIPresShell; michael@0: michael@0: class nsViewManager MOZ_FINAL michael@0: { michael@0: public: michael@0: friend class nsView; michael@0: michael@0: NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW michael@0: michael@0: NS_INLINE_DECL_REFCOUNTING(nsViewManager) michael@0: michael@0: nsViewManager(); michael@0: ~nsViewManager(); michael@0: michael@0: /** michael@0: * Initialize the ViewManager michael@0: * Note: this instance does not hold a reference to the presshell michael@0: * because it holds a reference to this instance. michael@0: * @result The result of the initialization, NS_OK if no errors michael@0: */ michael@0: nsresult Init(nsDeviceContext* aContext); michael@0: michael@0: /** michael@0: * Create an ordinary view michael@0: * @param aBounds initial bounds for view michael@0: * XXX We should eliminate this parameter; you can set the bounds after CreateView michael@0: * @param aParent intended parent for view. this is not actually set in the michael@0: * nsView through this method. it is only used by the initialization michael@0: * code to walk up the view tree, if necessary, to find resources. michael@0: * XXX We should eliminate this parameter! michael@0: * @param aVisibilityFlag initial visibility state of view michael@0: * XXX We should eliminate this parameter; you can set it after CreateView michael@0: * @result The new view. Never null. michael@0: */ michael@0: nsView* CreateView(const nsRect& aBounds, michael@0: nsView* aParent, michael@0: nsViewVisibility aVisibilityFlag = nsViewVisibility_kShow); michael@0: michael@0: /** michael@0: * Get the root of the view tree. michael@0: * @result the root view michael@0: */ michael@0: nsView* GetRootView() { return mRootView; } michael@0: michael@0: /** michael@0: * Set the root of the view tree. Does not destroy the current root view. michael@0: * aView may have a parent view managed by a different view manager. michael@0: * aView may have a widget (anything but printing) or may not (printing). michael@0: * @param aView view to set as root michael@0: */ michael@0: void SetRootView(nsView *aView); michael@0: michael@0: /** michael@0: * Get the dimensions of the root window. The dimensions are in michael@0: * twips michael@0: * @param aWidth out parameter for width of window in twips michael@0: * @param aHeight out parameter for height of window in twips michael@0: */ michael@0: void GetWindowDimensions(nscoord *aWidth, nscoord *aHeight); michael@0: michael@0: /** michael@0: * Set the dimensions of the root window. michael@0: * Called if the root window is resized. The dimensions are in michael@0: * twips michael@0: * @param aWidth of window in twips michael@0: * @param aHeight of window in twips michael@0: */ michael@0: void SetWindowDimensions(nscoord aWidth, nscoord aHeight); michael@0: michael@0: /** michael@0: * Do any resizes that are pending. michael@0: */ michael@0: void FlushDelayedResize(bool aDoReflow); michael@0: michael@0: /** michael@0: * Called to inform the view manager that the entire area of a view michael@0: * is dirty and needs to be redrawn. michael@0: * @param aView view to paint. should be root view michael@0: */ michael@0: void InvalidateView(nsView *aView); michael@0: michael@0: /** michael@0: * Called to inform the view manager that some portion of a view is dirty and michael@0: * needs to be redrawn. The rect passed in should be in the view's coordinate michael@0: * space. Does not check for paint suppression. michael@0: * @param aView view to paint. should be root view michael@0: * @param rect rect to mark as damaged michael@0: */ michael@0: void InvalidateViewNoSuppression(nsView *aView, const nsRect &aRect); michael@0: michael@0: /** michael@0: * Called to inform the view manager that it should invalidate all views. michael@0: */ michael@0: void InvalidateAllViews(); michael@0: michael@0: /** michael@0: * Called to dispatch an event to the appropriate view. Often called michael@0: * as a result of receiving a mouse or keyboard event from the widget michael@0: * event system. michael@0: * @param aEvent event to dispatch michael@0: * @param aViewTarget dispatch the event to this view michael@0: * @param aStatus event handling status michael@0: */ michael@0: void DispatchEvent(mozilla::WidgetGUIEvent *aEvent, michael@0: nsView* aViewTarget, michael@0: nsEventStatus* aStatus); michael@0: michael@0: /** michael@0: * Given a parent view, insert another view as its child. michael@0: * aSibling and aAbove control the "document order" for the insertion. michael@0: * If aSibling is null, the view is inserted at the end of the document order michael@0: * if aAfter is true, otherwise it is inserted at the beginning. michael@0: * If aSibling is non-null, then if aAfter is true, the view is inserted michael@0: * after the sibling in document order (appearing above the sibling unless michael@0: * overriden by z-order). michael@0: * If it is false, the view is inserted before the sibling. michael@0: * The view manager generates the appopriate dirty regions. michael@0: * @param aParent parent view michael@0: * @param aChild child view michael@0: * @param aSibling sibling view michael@0: * @param aAfter after or before in the document order michael@0: */ michael@0: void InsertChild(nsView *aParent, nsView *aChild, nsView *aSibling, michael@0: bool aAfter); michael@0: michael@0: void InsertChild(nsView *aParent, nsView *aChild, int32_t aZIndex); michael@0: michael@0: /** michael@0: * Remove a specific child view from its parent. This will NOT remove its placeholder michael@0: * if there is one. michael@0: * The view manager generates the appropriate dirty regions. michael@0: * @param aParent parent view michael@0: * @param aChild child view michael@0: */ michael@0: void RemoveChild(nsView *aChild); michael@0: michael@0: /** michael@0: * Move a view to the specified position, provided in parent coordinates. michael@0: * The new position is the (0, 0) origin for the view's coordinate system. michael@0: * The view's bounds may extend above or to the left of this point. michael@0: * The view manager generates the appropriate dirty regions. michael@0: * @param aView view to move michael@0: * @param aX x value for new view position michael@0: * @param aY y value for new view position michael@0: */ michael@0: void MoveViewTo(nsView *aView, nscoord aX, nscoord aY); michael@0: michael@0: /** michael@0: * Resize a view. In addition to setting the width and height, you can michael@0: * set the x and y of its bounds relative to its position. Negative x and y michael@0: * will let the view extend above and to the left of the (0,0) point in its michael@0: * coordinate system. michael@0: * The view manager generates the appropriate dirty regions. michael@0: * @param aView view to move michael@0: * @param the new bounds relative to the current position michael@0: * @param RepaintExposedAreaOnly michael@0: * if true Repaint only the expanded or contracted region, michael@0: * if false Repaint the union of the old and new rectangles. michael@0: */ michael@0: void ResizeView(nsView *aView, const nsRect &aRect, michael@0: bool aRepaintExposedAreaOnly = false); michael@0: michael@0: /** michael@0: * Set the visibility of a view. Hidden views have the effect of hiding michael@0: * their descendants as well. This does not affect painting, so layout michael@0: * is responsible for ensuring that content in hidden views is not michael@0: * painted nor handling events. It does affect the visibility of widgets; michael@0: * if a view is hidden, descendant views with widgets have their widgets michael@0: * hidden. michael@0: * The view manager generates the appropriate dirty regions. michael@0: * @param aView view to change visibility state of michael@0: * @param visible new visibility state michael@0: */ michael@0: void SetViewVisibility(nsView *aView, nsViewVisibility aVisible); michael@0: michael@0: /** michael@0: * Set the z-index of a view. Positive z-indices mean that a view michael@0: * is above its parent in z-order. Negative z-indices mean that a michael@0: * view is below its parent. michael@0: * The view manager generates the appropriate dirty regions. michael@0: * @param aAutoZIndex indicate that the z-index of a view is "auto". An "auto" z-index michael@0: * means that the view does not define a new stacking context, michael@0: * which means that the z-indicies of the view's children are michael@0: * relative to the view's siblings. michael@0: * @param aView view to change z depth of michael@0: * @param aZindex explicit z depth michael@0: */ michael@0: void SetViewZIndex(nsView *aView, bool aAutoZIndex, int32_t aZindex); michael@0: michael@0: /** michael@0: * Set whether the view "floats" above all other views, michael@0: * which tells the compositor not to consider higher views in michael@0: * the view hierarchy that would geometrically intersect with michael@0: * this view. This is a hack, but it fixes some problems with michael@0: * views that need to be drawn in front of all other views. michael@0: */ michael@0: void SetViewFloating(nsView *aView, bool aFloatingView); michael@0: michael@0: /** michael@0: * Set the presshell associated with this manager michael@0: * @param aPresShell - new presshell michael@0: */ michael@0: void SetPresShell(nsIPresShell *aPresShell) { mPresShell = aPresShell; } michael@0: michael@0: /** michael@0: * Get the pres shell associated with this manager michael@0: */ michael@0: nsIPresShell* GetPresShell() { return mPresShell; } michael@0: michael@0: /** michael@0: * Get the device context associated with this manager michael@0: */ michael@0: nsDeviceContext* GetDeviceContext() const michael@0: { michael@0: return mContext; michael@0: } michael@0: michael@0: /** michael@0: * A stack class for disallowing changes that would enter painting. For michael@0: * example, popup widgets shouldn't be resized during reflow, since doing so michael@0: * might cause synchronous painting inside reflow which is forbidden. michael@0: * While refresh is disabled, widget geometry changes are deferred and will michael@0: * be handled later, either from the refresh driver or from an NS_WILL_PAINT michael@0: * event. michael@0: * We don't want to defer widget geometry changes all the time. Resizing a michael@0: * popup from script doesn't need to be deferred, for example, especially michael@0: * since popup widget geometry is observable from script and expected to michael@0: * update synchronously. michael@0: */ michael@0: class MOZ_STACK_CLASS AutoDisableRefresh { michael@0: public: michael@0: AutoDisableRefresh(nsViewManager* aVM) { michael@0: if (aVM) { michael@0: mRootVM = aVM->IncrementDisableRefreshCount(); michael@0: } michael@0: } michael@0: ~AutoDisableRefresh() { michael@0: if (mRootVM) { michael@0: mRootVM->DecrementDisableRefreshCount(); michael@0: } michael@0: } michael@0: private: michael@0: AutoDisableRefresh(const AutoDisableRefresh& aOther); michael@0: const AutoDisableRefresh& operator=(const AutoDisableRefresh& aOther); michael@0: michael@0: nsRefPtr mRootVM; michael@0: }; michael@0: michael@0: private: michael@0: friend class AutoDisableRefresh; michael@0: michael@0: nsViewManager* IncrementDisableRefreshCount(); michael@0: void DecrementDisableRefreshCount(); michael@0: michael@0: public: michael@0: /** michael@0: * Retrieve the widget at the root of the nearest enclosing michael@0: * view manager whose root view has a widget. michael@0: */ michael@0: void GetRootWidget(nsIWidget **aWidget); michael@0: michael@0: /** michael@0: * Indicate whether the viewmanager is currently painting michael@0: * michael@0: * @param aPainting true if the viewmanager is painting michael@0: * false otherwise michael@0: */ michael@0: void IsPainting(bool& aIsPainting); michael@0: michael@0: /** michael@0: * Retrieve the time of the last user event. User events michael@0: * include mouse and keyboard events. The viewmanager michael@0: * saves the time of the last user event. michael@0: * michael@0: * @param aTime Last user event time in microseconds michael@0: */ michael@0: void GetLastUserEventTime(uint32_t& aTime); michael@0: michael@0: /** michael@0: * Find the nearest display root view for the view aView. This is the view for michael@0: * the nearest enclosing popup or the root view for the root document. michael@0: */ michael@0: static nsView* GetDisplayRootFor(nsView* aView); michael@0: michael@0: /** michael@0: * Flush the accumulated dirty region to the widget and update widget michael@0: * geometry. michael@0: */ michael@0: void ProcessPendingUpdates(); michael@0: michael@0: /** michael@0: * Just update widget geometry without flushing the dirty region michael@0: */ michael@0: void UpdateWidgetGeometry(); michael@0: michael@0: int32_t AppUnitsPerDevPixel() const michael@0: { michael@0: return mContext->AppUnitsPerDevPixel(); michael@0: } michael@0: michael@0: private: michael@0: static uint32_t gLastUserEventTime; michael@0: michael@0: /* Update the cached RootViewManager pointer on this view manager. */ michael@0: void InvalidateHierarchy(); michael@0: void FlushPendingInvalidates(); michael@0: michael@0: void ProcessPendingUpdatesForView(nsView *aView, michael@0: bool aFlushDirtyRegion = true); michael@0: void ProcessPendingUpdatesRecurse(nsView* aView, michael@0: nsTArray >& aWidgets); michael@0: void ProcessPendingUpdatesPaint(nsIWidget* aWidget); michael@0: michael@0: void FlushDirtyRegionToWidget(nsView* aView); michael@0: /** michael@0: * Call WillPaint() on all view observers under this vm root. michael@0: */ michael@0: void CallWillPaintOnObservers(); michael@0: void ReparentChildWidgets(nsView* aView, nsIWidget *aNewWidget); michael@0: void ReparentWidgets(nsView* aView, nsView *aParent); michael@0: void InvalidateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion); michael@0: michael@0: void InvalidateViews(nsView *aView); michael@0: michael@0: // aView is the view for aWidget and aRegion is relative to aWidget. michael@0: void Refresh(nsView *aView, const nsIntRegion& aRegion); michael@0: michael@0: // Utilities michael@0: michael@0: bool IsViewInserted(nsView *aView); michael@0: michael@0: /** michael@0: * Intersects aRect with aView's bounds and then transforms it from aView's michael@0: * coordinate system to the coordinate system of the widget attached to michael@0: * aView. michael@0: */ michael@0: nsIntRect ViewToWidget(nsView *aView, const nsRect &aRect) const; michael@0: michael@0: void DoSetWindowDimensions(nscoord aWidth, nscoord aHeight); michael@0: michael@0: bool IsPainting() const { michael@0: return RootViewManager()->mPainting; michael@0: } michael@0: michael@0: void SetPainting(bool aPainting) { michael@0: RootViewManager()->mPainting = aPainting; michael@0: } michael@0: michael@0: void InvalidateView(nsView *aView, const nsRect &aRect); michael@0: michael@0: nsViewManager* RootViewManager() const { return mRootViewManager; } michael@0: bool IsRootVM() const { return this == RootViewManager(); } michael@0: michael@0: // Whether synchronous painting is allowed at the moment. For example, michael@0: // widget geometry changes can cause synchronous painting, so they need to michael@0: // be deferred while refresh is disabled. michael@0: bool IsPaintingAllowed() { return RootViewManager()->mRefreshDisableCount == 0; } michael@0: michael@0: void WillPaintWindow(nsIWidget* aWidget); michael@0: bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion); michael@0: void DidPaintWindow(); michael@0: michael@0: // Call this when you need to let the viewmanager know that it now has michael@0: // pending updates. michael@0: void PostPendingUpdate(); michael@0: michael@0: nsRefPtr mContext; michael@0: nsIPresShell *mPresShell; michael@0: michael@0: // The size for a resize that we delayed until the root view becomes michael@0: // visible again. michael@0: nsSize mDelayedResize; michael@0: michael@0: nsView *mRootView; michael@0: // mRootViewManager is a strong ref unless it equals |this|. It's michael@0: // never null (if we have no ancestors, it will be |this|). michael@0: nsViewManager *mRootViewManager; michael@0: michael@0: // The following members should not be accessed directly except by michael@0: // the root view manager. Some have accessor functions to enforce michael@0: // this, as noted. michael@0: michael@0: int32_t mRefreshDisableCount; michael@0: // Use IsPainting() and SetPainting() to access mPainting. michael@0: bool mPainting; michael@0: bool mRecursiveRefreshPending; michael@0: bool mHasPendingWidgetGeometryChanges; michael@0: bool mInScroll; michael@0: michael@0: //from here to public should be static and locked... MMP michael@0: static int32_t mVMCount; //number of viewmanagers michael@0: michael@0: //list of view managers michael@0: static nsVoidArray *gViewManagers; michael@0: }; michael@0: michael@0: /** michael@0: Invalidation model: michael@0: michael@0: 1) Callers call into the view manager and ask it to invalidate a view. michael@0: michael@0: 2) The view manager finds the "right" widget for the view, henceforth called michael@0: the root widget. michael@0: michael@0: 3) The view manager traverses descendants of the root widget and for each michael@0: one that needs invalidation stores the rect to invalidate on the widget's michael@0: view (batching). michael@0: michael@0: 4) The dirty region is flushed to the right widget when michael@0: ProcessPendingUpdates is called from the RefreshDriver. michael@0: michael@0: It's important to note that widgets associated to views outside this view michael@0: manager can end up being invalidated during step 3. Therefore, the end of a michael@0: view update batch really needs to traverse the entire view tree, to ensure michael@0: that those invalidates happen. michael@0: michael@0: To cope with this, invalidation processing and should only happen on the michael@0: root viewmanager. michael@0: */ michael@0: michael@0: #endif // nsViewManager_h___