Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | <!-- This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | - License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> |
michael@0 | 4 | |
michael@0 | 5 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
michael@0 | 6 | <html> |
michael@0 | 7 | <head> |
michael@0 | 8 | |
michael@0 | 9 | <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"> |
michael@0 | 10 | <title>Detailed Design Template</title> |
michael@0 | 11 | </head> |
michael@0 | 12 | <body> |
michael@0 | 13 | |
michael@0 | 14 | <h1><font color="#cc0000">Gecko Layout Detailed Design Document</font></h1> |
michael@0 | 15 | |
michael@0 | 16 | <h1>Space Manager Detailed Design</h1> |
michael@0 | 17 | |
michael@0 | 18 | <h2>Overview</h2> |
michael@0 | 19 | <p> |
michael@0 | 20 | The Space Manager and related classes and structures are an important of |
michael@0 | 21 | the Gecko Layout system, specifically Block Layout. See the High Level |
michael@0 | 22 | Design document for an overview of the Space Manager, and as an introduction |
michael@0 | 23 | to the classes, structures and algorithms container in this, the Detailed |
michael@0 | 24 | Design Document. |
michael@0 | 25 | </p> |
michael@0 | 26 | |
michael@0 | 27 | |
michael@0 | 28 | |
michael@0 | 29 | <hr width="100%" size="2"> |
michael@0 | 30 | <h2>nsSpaceManager</h2> |
michael@0 | 31 | <p> |
michael@0 | 32 | The Space Manager is the central class is a group of classes that manage |
michael@0 | 33 | the occupied and available space that exists in horizontal bands across |
michael@0 | 34 | a canvas. The primary goal of the Space Manager is to provide information |
michael@0 | 35 | about those bands of space to support the CSS notion of floated elements. |
michael@0 | 36 | </p> |
michael@0 | 37 | |
michael@0 | 38 | <p> |
michael@0 | 39 | There are three important parts to the Space Manager API: the parts that |
michael@0 | 40 | deal with the coordinate space of the Space Manager, the parts that deal with |
michael@0 | 41 | the regions managed by the Space Manager, and the parts that manage float |
michael@0 | 42 | impact intervals. |
michael@0 | 43 | </p> |
michael@0 | 44 | |
michael@0 | 45 | <p> |
michael@0 | 46 | The class nsSpaceManager is declared in the file <a href="http://lxr.mozilla.org/seamonkey/source/layout/base/src/nsSpaceManager.h"> |
michael@0 | 47 | nsSpaceManger.h</a> |
michael@0 | 48 | . The class is only used in the layout module and cannot be exported |
michael@0 | 49 | outside of that module (nor does it need to be). It is not a general |
michael@0 | 50 | purpose class, and is not intended to be subclasses<font color="#cc0000"> |
michael@0 | 51 | .</font> |
michael@0 | 52 | </p> |
michael@0 | 53 | |
michael@0 | 54 | <p> |
michael@0 | 55 | Here is the class declaration, taken from the source file as of 01.08.02 |
michael@0 | 56 | </p> |
michael@0 | 57 | |
michael@0 | 58 | |
michael@0 | 59 | |
michael@0 | 60 | <pre>/** |
michael@0 | 61 | * Class for dealing with bands of available space. The space manager |
michael@0 | 62 | * defines a coordinate space with an origin at (0, 0) that grows down |
michael@0 | 63 | * and to the right. |
michael@0 | 64 | */ |
michael@0 | 65 | class nsSpaceManager { |
michael@0 | 66 | public: |
michael@0 | 67 | nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame); |
michael@0 | 68 | ~nsSpaceManager(); |
michael@0 | 69 | |
michael@0 | 70 | void* operator new(size_t aSize); |
michael@0 | 71 | void operator delete(void* aPtr, size_t aSize); |
michael@0 | 72 | |
michael@0 | 73 | static void Shutdown(); |
michael@0 | 74 | |
michael@0 | 75 | /* |
michael@0 | 76 | * Get the frame that's associated with the space manager. This frame |
michael@0 | 77 | * created the space manager, and the world coordinate space is |
michael@0 | 78 | * relative to this frame. |
michael@0 | 79 | * |
michael@0 | 80 | * You can use QueryInterface() on this frame to get any additional |
michael@0 | 81 | * interfaces. |
michael@0 | 82 | */ |
michael@0 | 83 | nsIFrame* GetFrame() const { return mFrame; } |
michael@0 | 84 | |
michael@0 | 85 | /** |
michael@0 | 86 | * Translate the current origin by the specified (dx, dy). This |
michael@0 | 87 | * creates a new local coordinate space relative to the current |
michael@0 | 88 | * coordinate space. |
michael@0 | 89 | */ |
michael@0 | 90 | void Translate(nscoord aDx, nscoord aDy) { mX += aDx; mY += aDy; } |
michael@0 | 91 | |
michael@0 | 92 | /** |
michael@0 | 93 | * Returns the current translation from local coordinate space to |
michael@0 | 94 | * world coordinate space. This represents the accumulated calls to |
michael@0 | 95 | * Translate(). |
michael@0 | 96 | */ |
michael@0 | 97 | void GetTranslation(nscoord& aX, nscoord& aY) const { aX = mX; aY = mY; } |
michael@0 | 98 | |
michael@0 | 99 | /** |
michael@0 | 100 | * Returns the y-most of the bottommost band or 0 if there are no bands. |
michael@0 | 101 | * |
michael@0 | 102 | * @return PR_TRUE if there are bands and PR_FALSE if there are no bands |
michael@0 | 103 | */ |
michael@0 | 104 | PRBool YMost(nscoord& aYMost) const; |
michael@0 | 105 | |
michael@0 | 106 | /** |
michael@0 | 107 | * Returns a band starting at the specified y-offset. The band data |
michael@0 | 108 | * indicates which parts of the band are available, and which parts |
michael@0 | 109 | * are unavailable |
michael@0 | 110 | * |
michael@0 | 111 | * The band data that is returned is in the coordinate space of the |
michael@0 | 112 | * local coordinate system. |
michael@0 | 113 | * |
michael@0 | 114 | * The local coordinate space origin, the y-offset, and the max size |
michael@0 | 115 | * describe a rectangle that's used to clip the underlying band of |
michael@0 | 116 | * available space, i.e. |
michael@0 | 117 | * {0, aYOffset, aMaxSize.width, aMaxSize.height} in the local |
michael@0 | 118 | * coordinate space |
michael@0 | 119 | * |
michael@0 | 120 | * @param aYOffset the y-offset of where the band begins. The coordinate is |
michael@0 | 121 | * relative to the upper-left corner of the local coordinate space |
michael@0 | 122 | * @param aMaxSize the size to use to constrain the band data |
michael@0 | 123 | * @param aBandData [in,out] used to return the list of trapezoids that |
michael@0 | 124 | * describe the available space and the unavailable space |
michael@0 | 125 | * @return NS_OK if successful and NS_ERROR_FAILURE if the band data is not |
michael@0 | 126 | * not large enough. The 'count' member of the band data struct |
michael@0 | 127 | * indicates how large the array of trapezoids needs to be |
michael@0 | 128 | */ |
michael@0 | 129 | nsresult GetBandData(nscoord aYOffset, |
michael@0 | 130 | const nsSize& aMaxSize, |
michael@0 | 131 | nsBandData& aBandData) const; |
michael@0 | 132 | |
michael@0 | 133 | /** |
michael@0 | 134 | * Add a rectangular region of unavailable space. The space is |
michael@0 | 135 | * relative to the local coordinate system. |
michael@0 | 136 | * |
michael@0 | 137 | * The region is tagged with a frame |
michael@0 | 138 | * |
michael@0 | 139 | * @param aFrame the frame used to identify the region. Must not be NULL |
michael@0 | 140 | * @param aUnavailableSpace the bounding rect of the unavailable space |
michael@0 | 141 | * @return NS_OK if successful |
michael@0 | 142 | * NS_ERROR_FAILURE if there is already a region tagged with aFrame |
michael@0 | 143 | */ |
michael@0 | 144 | nsresult AddRectRegion(nsIFrame* aFrame, |
michael@0 | 145 | const nsRect& aUnavailableSpace); |
michael@0 | 146 | |
michael@0 | 147 | /** |
michael@0 | 148 | * Resize the rectangular region associated with aFrame by the specified |
michael@0 | 149 | * deltas. The height change always applies to the bottom edge or the existing |
michael@0 | 150 | * rect. You specify whether the width change applies to the left or right edge |
michael@0 | 151 | * |
michael@0 | 152 | * Returns NS_OK if successful, NS_ERROR_INVALID_ARG if there is no region |
michael@0 | 153 | * tagged with aFrame |
michael@0 | 154 | */ |
michael@0 | 155 | enum AffectedEdge {LeftEdge, RightEdge}; |
michael@0 | 156 | nsresult ResizeRectRegion(nsIFrame* aFrame, |
michael@0 | 157 | nscoord aDeltaWidth, |
michael@0 | 158 | nscoord aDeltaHeight, |
michael@0 | 159 | AffectedEdge aEdge = RightEdge); |
michael@0 | 160 | |
michael@0 | 161 | /** |
michael@0 | 162 | * Offset the region associated with aFrame by the specified amount. |
michael@0 | 163 | * |
michael@0 | 164 | * Returns NS_OK if successful, NS_ERROR_INVALID_ARG if there is no region |
michael@0 | 165 | * tagged with aFrame |
michael@0 | 166 | */ |
michael@0 | 167 | nsresult OffsetRegion(nsIFrame* aFrame, nscoord dx, nscoord dy); |
michael@0 | 168 | |
michael@0 | 169 | /** |
michael@0 | 170 | * Remove the region associated with aFrane. |
michael@0 | 171 | * |
michael@0 | 172 | * Returns NS_OK if successful and NS_ERROR_INVALID_ARG if there is no region |
michael@0 | 173 | * tagged with aFrame |
michael@0 | 174 | */ |
michael@0 | 175 | nsresult RemoveRegion(nsIFrame* aFrame); |
michael@0 | 176 | |
michael@0 | 177 | /** |
michael@0 | 178 | * Clears the list of regions representing the unavailable space. |
michael@0 | 179 | */ |
michael@0 | 180 | void ClearRegions(); |
michael@0 | 181 | |
michael@0 | 182 | /** |
michael@0 | 183 | * Methods for dealing with the propagation of float damage during |
michael@0 | 184 | * reflow. |
michael@0 | 185 | */ |
michael@0 | 186 | PRBool HasFloatDamage() |
michael@0 | 187 | { |
michael@0 | 188 | return !mFloatDamage.IsEmpty(); |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) |
michael@0 | 192 | { |
michael@0 | 193 | mFloatDamage.IncludeInterval(aIntervalBegin + mY, aIntervalEnd + mY); |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | PRBool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) |
michael@0 | 197 | { |
michael@0 | 198 | return mFloatDamage.Intersects(aIntervalBegin + mY, aIntervalEnd + mY); |
michael@0 | 199 | } |
michael@0 | 200 | |
michael@0 | 201 | #ifdef DEBUG |
michael@0 | 202 | /** |
michael@0 | 203 | * Dump the state of the spacemanager out to a file |
michael@0 | 204 | */ |
michael@0 | 205 | nsresult List(FILE* out); |
michael@0 | 206 | |
michael@0 | 207 | void SizeOf(nsISizeOfHandler* aHandler, uint32_t* aResult) const; |
michael@0 | 208 | #endif |
michael@0 | 209 | |
michael@0 | 210 | private: |
michael@0 | 211 | // Structure that maintains information about the region associated |
michael@0 | 212 | // with a particular frame |
michael@0 | 213 | struct FrameInfo { |
michael@0 | 214 | nsIFrame* const mFrame; |
michael@0 | 215 | nsRect mRect; // rectangular region |
michael@0 | 216 | FrameInfo* mNext; |
michael@0 | 217 | |
michael@0 | 218 | FrameInfo(nsIFrame* aFrame, const nsRect& aRect); |
michael@0 | 219 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 220 | ~FrameInfo(); |
michael@0 | 221 | #endif |
michael@0 | 222 | }; |
michael@0 | 223 | |
michael@0 | 224 | // Doubly linked list of band rects |
michael@0 | 225 | struct BandRect : PRCListStr { |
michael@0 | 226 | nscoord mLeft, mTop; |
michael@0 | 227 | nscoord mRight, mBottom; |
michael@0 | 228 | int32_t mNumFrames; // number of frames occupying this rect |
michael@0 | 229 | union { |
michael@0 | 230 | nsIFrame* mFrame; // single frame occupying the space |
michael@0 | 231 | nsVoidArray* mFrames; // list of frames occupying the space |
michael@0 | 232 | }; |
michael@0 | 233 | |
michael@0 | 234 | BandRect(nscoord aLeft, nscoord aTop, |
michael@0 | 235 | nscoord aRight, nscoord aBottom, |
michael@0 | 236 | nsIFrame*); |
michael@0 | 237 | BandRect(nscoord aLeft, nscoord aTop, |
michael@0 | 238 | nscoord aRight, nscoord aBottom, |
michael@0 | 239 | nsVoidArray*); |
michael@0 | 240 | ~BandRect(); |
michael@0 | 241 | |
michael@0 | 242 | // List operations |
michael@0 | 243 | BandRect* Next() const {return (BandRect*)PR_NEXT_LINK(this);} |
michael@0 | 244 | BandRect* Prev() const {return (BandRect*)PR_PREV_LINK(this);} |
michael@0 | 245 | void InsertBefore(BandRect* aBandRect) {PR_INSERT_BEFORE(aBandRect, this);} |
michael@0 | 246 | void InsertAfter(BandRect* aBandRect) {PR_INSERT_AFTER(aBandRect, this);} |
michael@0 | 247 | void Remove() {PR_REMOVE_LINK(this);} |
michael@0 | 248 | |
michael@0 | 249 | // Split the band rect into two vertically, with this band rect becoming |
michael@0 | 250 | // the top part, and a new band rect being allocated and returned for the |
michael@0 | 251 | // bottom part |
michael@0 | 252 | // |
michael@0 | 253 | // Does not insert the new band rect into the linked list |
michael@0 | 254 | BandRect* SplitVertically(nscoord aBottom); |
michael@0 | 255 | |
michael@0 | 256 | // Split the band rect into two horizontally, with this band rect becoming |
michael@0 | 257 | // the left part, and a new band rect being allocated and returned for the |
michael@0 | 258 | // right part |
michael@0 | 259 | // |
michael@0 | 260 | // Does not insert the new band rect into the linked list |
michael@0 | 261 | BandRect* SplitHorizontally(nscoord aRight); |
michael@0 | 262 | |
michael@0 | 263 | // Accessor functions |
michael@0 | 264 | PRBool IsOccupiedBy(const nsIFrame*) const; |
michael@0 | 265 | void AddFrame(const nsIFrame*); |
michael@0 | 266 | void RemoveFrame(const nsIFrame*); |
michael@0 | 267 | PRBool HasSameFrameList(const BandRect* aBandRect) const; |
michael@0 | 268 | int32_t Length() const; |
michael@0 | 269 | }; |
michael@0 | 270 | |
michael@0 | 271 | // Circular linked list of band rects |
michael@0 | 272 | struct BandList : BandRect { |
michael@0 | 273 | BandList(); |
michael@0 | 274 | |
michael@0 | 275 | // Accessors |
michael@0 | 276 | PRBool IsEmpty() const {return PR_CLIST_IS_EMPTY((PRCListStr*)this);} |
michael@0 | 277 | BandRect* Head() const {return (BandRect*)PR_LIST_HEAD(this);} |
michael@0 | 278 | BandRect* Tail() const {return (BandRect*)PR_LIST_TAIL(this);} |
michael@0 | 279 | |
michael@0 | 280 | // Operations |
michael@0 | 281 | void Append(BandRect* aBandRect) {PR_APPEND_LINK(aBandRect, this);} |
michael@0 | 282 | |
michael@0 | 283 | // Remove and delete all the band rects in the list |
michael@0 | 284 | void Clear(); |
michael@0 | 285 | }; |
michael@0 | 286 | |
michael@0 | 287 | |
michael@0 | 288 | FrameInfo* GetFrameInfoFor(nsIFrame* aFrame); |
michael@0 | 289 | FrameInfo* CreateFrameInfo(nsIFrame* aFrame, const nsRect& aRect); |
michael@0 | 290 | void DestroyFrameInfo(FrameInfo*); |
michael@0 | 291 | |
michael@0 | 292 | void ClearFrameInfo(); |
michael@0 | 293 | void ClearBandRects(); |
michael@0 | 294 | |
michael@0 | 295 | BandRect* GetNextBand(const BandRect* aBandRect) const; |
michael@0 | 296 | void DivideBand(BandRect* aBand, nscoord aBottom); |
michael@0 | 297 | PRBool CanJoinBands(BandRect* aBand, BandRect* aPrevBand); |
michael@0 | 298 | PRBool JoinBands(BandRect* aBand, BandRect* aPrevBand); |
michael@0 | 299 | void AddRectToBand(BandRect* aBand, BandRect* aBandRect); |
michael@0 | 300 | void InsertBandRect(BandRect* aBandRect); |
michael@0 | 301 | |
michael@0 | 302 | nsresult GetBandAvailableSpace(const BandRect* aBand, |
michael@0 | 303 | nscoord aY, |
michael@0 | 304 | const nsSize& aMaxSize, |
michael@0 | 305 | nsBandData& aAvailableSpace) const; |
michael@0 | 306 | |
michael@0 | 307 | nsIFrame* const mFrame; // frame associated with the space manager |
michael@0 | 308 | nscoord mX, mY; // translation from local to global coordinate space |
michael@0 | 309 | BandList mBandList; // header/sentinel for circular linked list of band rects |
michael@0 | 310 | FrameInfo* mFrameInfoMap; |
michael@0 | 311 | nsIntervalSet mFloatDamage; |
michael@0 | 312 | |
michael@0 | 313 | static int32_t sCachedSpaceManagerCount; |
michael@0 | 314 | static void* sCachedSpaceManagers[NS_SPACE_MANAGER_CACHE_SIZE]; |
michael@0 | 315 | |
michael@0 | 316 | nsSpaceManager(const nsSpaceManager&); // no implementation |
michael@0 | 317 | void operator=(const nsSpaceManager&); // no implementation |
michael@0 | 318 | }; |
michael@0 | 319 | |
michael@0 | 320 | </pre> |
michael@0 | 321 | |
michael@0 | 322 | <h3>Public API</h3> |
michael@0 | 323 | |
michael@0 | 324 | <h4>Life Cycle:</h4> |
michael@0 | 325 | <p> |
michael@0 | 326 | The Constructor requires a Presentation Shell, used for arena allocations |
michael@0 | 327 | mostly, and a frame that this Space Manager is rooted on. The coordinate |
michael@0 | 328 | space of this Space Manager is relative to the frame passed in to the constructor. |
michael@0 | 329 | </p> |
michael@0 | 330 | |
michael@0 | 331 | <pre> nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame); |
michael@0 | 332 | ~nsSpaceManager(); |
michael@0 | 333 | </pre> |
michael@0 | 334 | <p> |
michael@0 | 335 | Operators 'new' and 'delete' are overridden to support a recycler. Space |
michael@0 | 336 | Manager instances come and go pretty frequently, and this recycler prevents |
michael@0 | 337 | excessive heap allocations and the performance penalties associated with |
michael@0 | 338 | it. The #define NS_SPACE_MANAGER_CACHE_SIZE is used to control the number |
michael@0 | 339 | of Space Manager instances that can be present in the recycler, currently |
michael@0 | 340 | 4. If more than NS_SPACE_MANAGER_CACHE_SIZE are allocated at a time, |
michael@0 | 341 | then standard allocation is used. |
michael@0 | 342 | </p> |
michael@0 | 343 | |
michael@0 | 344 | <pre> |
michael@0 | 345 | void* operator new(size_t aSize); |
michael@0 | 346 | void operator delete(void* aPtr, size_t aSize); |
michael@0 | 347 | |
michael@0 | 348 | </pre> |
michael@0 | 349 | <p> |
michael@0 | 350 | A Static method is used to shutdown the Space Manager recycling. This |
michael@0 | 351 | method deletes all of the Space Mangers inthe recycler,and prevents further |
michael@0 | 352 | recycling. It is meant to be called only when the layout module is being |
michael@0 | 353 | terminated. |
michael@0 | 354 | </p> |
michael@0 | 355 | |
michael@0 | 356 | <pre> static void Shutdown(); |
michael@0 | 357 | |
michael@0 | 358 | </pre> |
michael@0 | 359 | |
michael@0 | 360 | <h4>Origin / Coordinate Space Translation</h4> |
michael@0 | 361 | |
michael@0 | 362 | <pre> /** |
michael@0 | 363 | * Translate the current origin by the specified (dx, dy). This |
michael@0 | 364 | * creates a new local coordinate space relative to the current |
michael@0 | 365 | * coordinate space. |
michael@0 | 366 | */ |
michael@0 | 367 | void Translate(nscoord aDx, nscoord aDy) { mX += aDx; mY += aDy; } |
michael@0 | 368 | |
michael@0 | 369 | /** |
michael@0 | 370 | * Returns the current translation from local coordinate space to |
michael@0 | 371 | * world coordinate space. This represents the accumulated calls to |
michael@0 | 372 | * Translate(). |
michael@0 | 373 | */ |
michael@0 | 374 | void GetTranslation(nscoord& aX, nscoord& aY) const { aX = mX; aY = mY; } |
michael@0 | 375 | |
michael@0 | 376 | /** |
michael@0 | 377 | * Returns the y-most of the bottommost band or 0 if there are no bands. |
michael@0 | 378 | * |
michael@0 | 379 | * @return PR_TRUE if there are bands and PR_FALSE if there are no bands |
michael@0 | 380 | */ |
michael@0 | 381 | PRBool YMost(nscoord& aYMost) const; |
michael@0 | 382 | </pre> |
michael@0 | 383 | |
michael@0 | 384 | <h4>Region Management</h4> |
michael@0 | 385 | |
michael@0 | 386 | <pre> /** |
michael@0 | 387 | * Returns a band starting at the specified y-offset. The band data |
michael@0 | 388 | * indicates which parts of the band are available, and which parts |
michael@0 | 389 | * are unavailable |
michael@0 | 390 | * |
michael@0 | 391 | * The band data that is returned is in the coordinate space of the |
michael@0 | 392 | * local coordinate system. |
michael@0 | 393 | * |
michael@0 | 394 | * The local coordinate space origin, the y-offset, and the max size |
michael@0 | 395 | * describe a rectangle that's used to clip the underlying band of |
michael@0 | 396 | * available space, i.e. |
michael@0 | 397 | * {0, aYOffset, aMaxSize.width, aMaxSize.height} in the local |
michael@0 | 398 | * coordinate space |
michael@0 | 399 | * |
michael@0 | 400 | * @param aYOffset the y-offset of where the band begins. The coordinate is |
michael@0 | 401 | * relative to the upper-left corner of the local coordinate space |
michael@0 | 402 | * @param aMaxSize the size to use to constrain the band data |
michael@0 | 403 | * @param aBandData [in,out] used to return the list of trapezoids that |
michael@0 | 404 | * describe the available space and the unavailable space |
michael@0 | 405 | * @return NS_OK if successful and NS_ERROR_FAILURE if the band data is not |
michael@0 | 406 | * not large enough. The 'count' member of the band data struct |
michael@0 | 407 | * indicates how large the array of trapezoids needs to be |
michael@0 | 408 | */ |
michael@0 | 409 | nsresult GetBandData(nscoord aYOffset, |
michael@0 | 410 | const nsSize& aMaxSize, |
michael@0 | 411 | nsBandData& aBandData) const; |
michael@0 | 412 | |
michael@0 | 413 | /** |
michael@0 | 414 | * Add a rectangular region of unavailable space. The space is |
michael@0 | 415 | * relative to the local coordinate system. |
michael@0 | 416 | * |
michael@0 | 417 | * The region is tagged with a frame |
michael@0 | 418 | * |
michael@0 | 419 | * @param aFrame the frame used to identify the region. Must not be NULL |
michael@0 | 420 | * @param aUnavailableSpace the bounding rect of the unavailable space |
michael@0 | 421 | * @return NS_OK if successful |
michael@0 | 422 | * NS_ERROR_FAILURE if there is already a region tagged with aFrame |
michael@0 | 423 | */ |
michael@0 | 424 | nsresult AddRectRegion(nsIFrame* aFrame, |
michael@0 | 425 | const nsRect& aUnavailableSpace); |
michael@0 | 426 | |
michael@0 | 427 | /** |
michael@0 | 428 | * Resize the rectangular region associated with aFrame by the specified |
michael@0 | 429 | * deltas. The height change always applies to the bottom edge or the existing |
michael@0 | 430 | * rect. You specify whether the width change applies to the left or right edge |
michael@0 | 431 | * |
michael@0 | 432 | * Returns NS_OK if successful, NS_ERROR_INVALID_ARG if there is no region |
michael@0 | 433 | * tagged with aFrame |
michael@0 | 434 | */ |
michael@0 | 435 | enum AffectedEdge {LeftEdge, RightEdge}; |
michael@0 | 436 | nsresult ResizeRectRegion(nsIFrame* aFrame, |
michael@0 | 437 | nscoord aDeltaWidth, |
michael@0 | 438 | nscoord aDeltaHeight, |
michael@0 | 439 | AffectedEdge aEdge = RightEdge); |
michael@0 | 440 | |
michael@0 | 441 | /** |
michael@0 | 442 | * Offset the region associated with aFrame by the specified amount. |
michael@0 | 443 | * |
michael@0 | 444 | * Returns NS_OK if successful, NS_ERROR_INVALID_ARG if there is no region |
michael@0 | 445 | * tagged with aFrame |
michael@0 | 446 | */ |
michael@0 | 447 | nsresult OffsetRegion(nsIFrame* aFrame, nscoord dx, nscoord dy); |
michael@0 | 448 | |
michael@0 | 449 | /** |
michael@0 | 450 | * Remove the region associated with aFrane. |
michael@0 | 451 | * |
michael@0 | 452 | * Returns NS_OK if successful and NS_ERROR_INVALID_ARG if there is no region |
michael@0 | 453 | * tagged with aFrame |
michael@0 | 454 | */ |
michael@0 | 455 | nsresult RemoveRegion(nsIFrame* aFrame); |
michael@0 | 456 | |
michael@0 | 457 | /** |
michael@0 | 458 | * Clears the list of regions representing the unavailable space. |
michael@0 | 459 | */ |
michael@0 | 460 | void ClearRegions(); |
michael@0 | 461 | </pre> |
michael@0 | 462 | |
michael@0 | 463 | <h4>Float Impact</h4> |
michael@0 | 464 | |
michael@0 | 465 | <pre> /** |
michael@0 | 466 | * Methods for dealing with the propagation of float damage during |
michael@0 | 467 | * reflow. |
michael@0 | 468 | */ |
michael@0 | 469 | PRBool HasFloatDamage() |
michael@0 | 470 | { |
michael@0 | 471 | return !mFloatDamage.IsEmpty(); |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) |
michael@0 | 475 | { |
michael@0 | 476 | mFloatDamage.IncludeInterval(aIntervalBegin + mY, aIntervalEnd + mY); |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | PRBool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) |
michael@0 | 480 | { |
michael@0 | 481 | return mFloatDamage.Intersects(aIntervalBegin + mY, aIntervalEnd + mY); |
michael@0 | 482 | } |
michael@0 | 483 | </pre> |
michael@0 | 484 | |
michael@0 | 485 | <h4>Debug Only Methods</h4> |
michael@0 | 486 | |
michael@0 | 487 | <pre> /** |
michael@0 | 488 | * Dump the state of the spacemanager out to a file |
michael@0 | 489 | */ |
michael@0 | 490 | nsresult List(FILE* out); |
michael@0 | 491 | |
michael@0 | 492 | void SizeOf(nsISizeOfHandler* aHandler, uint32_t* aResult) const; |
michael@0 | 493 | |
michael@0 | 494 | </pre> |
michael@0 | 495 | |
michael@0 | 496 | <h4>Unused / Obsolete Methods</h4> |
michael@0 | 497 | |
michael@0 | 498 | <pre> /* |
michael@0 | 499 | * Get the frame that's associated with the space manager. This frame |
michael@0 | 500 | * created the space manager, and the world coordinate space is |
michael@0 | 501 | * relative to this frame. |
michael@0 | 502 | * |
michael@0 | 503 | * You can use QueryInterface() on this frame to get any additional |
michael@0 | 504 | * interfaces. |
michael@0 | 505 | */ |
michael@0 | 506 | nsIFrame* GetFrame() const { return mFrame; } |
michael@0 | 507 | |
michael@0 | 508 | </pre> |
michael@0 | 509 | |
michael@0 | 510 | <h3>Implementation Notes</h3> |
michael@0 | 511 | |
michael@0 | 512 | <h4></h4> |
michael@0 | 513 | |
michael@0 | 514 | <h4>Algorithm 1: GetBandData</h4> |
michael@0 | 515 | <p> |
michael@0 | 516 | GetBandData is used to provide information to clients about what space if |
michael@0 | 517 | available and unavailable in a band of space. The client provides a |
michael@0 | 518 | vertical offset, the yOffset, that corresponds to the band that is of interest. |
michael@0 | 519 | This will be the y offset of the frame that is being reflowed. The |
michael@0 | 520 | caller also provides a collection of BandData objects (an array) and the |
michael@0 | 521 | number of items that the collection can handle. If the collection is |
michael@0 | 522 | too small, then an error is returned and the count is updated to indicate |
michael@0 | 523 | the size required. |
michael@0 | 524 | </p> |
michael@0 | 525 | |
michael@0 | 526 | <p> |
michael@0 | 527 | The algorithm to provide the band data is as follows: |
michael@0 | 528 | </p> |
michael@0 | 529 | <ul> |
michael@0 | 530 | <li>Get a vertical offset in world coordinates (instead of frame-relative |
michael@0 | 531 | coordinates) by adding the y-origin of the SpaceManager to the y offset passed |
michael@0 | 532 | in</li> |
michael@0 | 533 | <li>If the (adjusted) y value passed in is greater than the greatest band |
michael@0 | 534 | being managed, then all space is available so a single trapezoid is returned, |
michael@0 | 535 | marked as available and sized to the maximum size value (passed in).</li> |
michael@0 | 536 | <li>If the (adjusted) y offset intersects a band, then gather the band |
michael@0 | 537 | data:</li> |
michael@0 | 538 | <ul> |
michael@0 | 539 | <li>walk the internal bandData list from head to tail</li> |
michael@0 | 540 | <li>for each band data entry, see if the top of the band is greater than |
michael@0 | 541 | the (adjusted) y offset requested</li> |
michael@0 | 542 | <li>if it is, then band is below the offset requested, so the area between |
michael@0 | 543 | the band and the y offset is available - create a trapezoid with that region |
michael@0 | 544 | and return it.</li> |
michael@0 | 545 | <li>if the (adjusted) y offset is between the band top and bottom, then |
michael@0 | 546 | get the available space for the band by calling GetBandAvailableSpace</li> |
michael@0 | 547 | <li>otherwise, move to the next band</li> |
michael@0 | 548 | </ul> |
michael@0 | 549 | </ul> |
michael@0 | 550 | <h5>GetBandAvailableSpace:</h5> |
michael@0 | 551 | This method is called from GetBandData only. It walks all of the bands in |
michael@0 | 552 | the space manager and determines which bands intersect with the band passed |
michael@0 | 553 | in, and if within those bands there are regions that are available or occupied. |
michael@0 | 554 | |
michael@0 | 555 | <ul> |
michael@0 | 556 | <li>First, walk all of the bands until a band that is to the right of the |
michael@0 | 557 | desired offset is located</li> |
michael@0 | 558 | <li>Starting at that band, walk the remaining bands:</li> |
michael@0 | 559 | <ul> |
michael@0 | 560 | <li>if the current band is to the right of the requested band, then there |
michael@0 | 561 | is available space. </li> |
michael@0 | 562 | <ul> |
michael@0 | 563 | <li>if there is more room in the bandData collection, then add a trapezoid |
michael@0 | 564 | to the bandData collection such that it is marked as available and has a |
michael@0 | 565 | rect that represents the space between the reference band tna dht band being |
michael@0 | 566 | examined</li> |
michael@0 | 567 | <li>if there is no more room in the BandData collection, estimate the |
michael@0 | 568 | number of entries requires as the current count + twice the number of bands |
michael@0 | 569 | below the reference band, plus two. Return an error code so the caller |
michael@0 | 570 | can reallocate the collection and try again.</li> |
michael@0 | 571 | </ul> |
michael@0 | 572 | <li>check the size of the collection again, if there is no room left |
michael@0 | 573 | then estimate the number of items requires as the current count + twice the |
michael@0 | 574 | number of bands below the band in question plus one. </li> |
michael@0 | 575 | <li>create a new trapezoid in the band collection that has a region corresponding |
michael@0 | 576 | to the reference band rect, marked as occupied by either a single or multiple |
michael@0 | 577 | frames.</li> |
michael@0 | 578 | <li>move to the next band</li> |
michael@0 | 579 | </ul> |
michael@0 | 580 | <li>after walking all of the band data, se if we have reached the right |
michael@0 | 581 | edge of the band. </li> |
michael@0 | 582 | <ul> |
michael@0 | 583 | <li>If not, then check for space in the band collection</li> |
michael@0 | 584 | <ul> |
michael@0 | 585 | <li>if there is no room left, then set the count to the current count |
michael@0 | 586 | plus 1 and return an error.</li> |
michael@0 | 587 | <li>otherwise, create another entry in the band collection, marked |
michael@0 | 588 | as available, and with a rect corresponding to the area remainin in the band |
michael@0 | 589 | (eg. from the right edge of the last band rect to the right edge of the band).</li> |
michael@0 | 590 | </ul> |
michael@0 | 591 | </ul> |
michael@0 | 592 | </ul> |
michael@0 | 593 | |
michael@0 | 594 | <h4>Algorithm 2: AddRectRegion</h4> |
michael@0 | 595 | Clients call into this method to notify the Space Manager that a new frame |
michael@0 | 596 | is occupying some space. |
michael@0 | 597 | |
michael@0 | 598 | <ul> |
michael@0 | 599 | <li>First, try to get frame info for the frame. If it is found, return |
michael@0 | 600 | an error since the frame is already associated with a region in the Space |
michael@0 | 601 | Manager.</li> |
michael@0 | 602 | <li>Next, create a rect from the occupied space passed in by the caller, |
michael@0 | 603 | transforming it first to world-coordinates by adding the Space Manager's |
michael@0 | 604 | offset to the occupied space rect passed in.</li> |
michael@0 | 605 | <li>Create a new Frame Info instance for the frame and rect, returning |
michael@0 | 606 | an error if allocation fails.</li> |
michael@0 | 607 | <li>Check if the occupied space rect (adjusted) is empty, if so, return |
michael@0 | 608 | an error (<font color="#cc0000">NOTE: this could be done earlier, or |
michael@0 | 609 | prevented by the caller</font>)</li> |
michael@0 | 610 | <li>Allocate a new BandRect instance with the rect and frame as constructor |
michael@0 | 611 | arguments, and insert it into the collection via InsertBandRect</li> |
michael@0 | 612 | </ul> |
michael@0 | 613 | <h5>InsertBandRect:</h5> |
michael@0 | 614 | Internal method to insert a band rect into the BandList in the correct location. |
michael@0 | 615 | There are several cases it has to handle, as specified in the source file |
michael@0 | 616 | comments: |
michael@0 | 617 | |
michael@0 | 618 | <pre>// When comparing a rect to a band there are seven cases to consider. |
michael@0 | 619 | // 'R' is the rect and 'B' is the band. |
michael@0 | 620 | // |
michael@0 | 621 | // Case 1 Case 2 Case 3 Case 4 |
michael@0 | 622 | // ------ ------ ------ ------ |
michael@0 | 623 | // +-----+ +-----+ +-----+ +-----+ |
michael@0 | 624 | // | R | | R | +-----+ +-----+ | | | | |
michael@0 | 625 | // +-----+ +-----+ | | | R | | B | | B | |
michael@0 | 626 | // +-----+ | B | +-----+ | | +-----+ | | |
michael@0 | 627 | // | | | | +-----+ | R | +-----+ |
michael@0 | 628 | // | B | +-----+ +-----+ |
michael@0 | 629 | // | | |
michael@0 | 630 | // +-----+ |
michael@0 | 631 | // |
michael@0 | 632 | // |
michael@0 | 633 | // |
michael@0 | 634 | // Case 5 Case 6 Case 7 |
michael@0 | 635 | // ------ ------ ------ |
michael@0 | 636 | // +-----+ +-----+ +-----+ +-----+ |
michael@0 | 637 | // | | | R | | B | | | +-----+ |
michael@0 | 638 | // | B | +-----+ +-----+ | R | | B | |
michael@0 | 639 | // | | | | +-----+ |
michael@0 | 640 | // +-----+ +-----+ |
michael@0 | 641 | // +-----+ |
michael@0 | 642 | // | R | |
michael@0 | 643 | // +-----+ |
michael@0 | 644 | // |
michael@0 | 645 | </pre> |
michael@0 | 646 | <ul> |
michael@0 | 647 | <li>First, check for the easiest case, where there are no existing band |
michael@0 | 648 | rects, or the band rect passed in is below the bottommost rect. In this case, |
michael@0 | 649 | just append the band rect and return.</li> |
michael@0 | 650 | <li>Starting at the head of the list of bandRects, check for intersection |
michael@0 | 651 | with the rect passed in:</li> |
michael@0 | 652 | <ul> |
michael@0 | 653 | <li>case #1: the rect is totally above the current band rect, so insert |
michael@0 | 654 | a new band rect before the current bandRect</li> |
michael@0 | 655 | <li>cases #2 and #7: the rect is partially above the band rect, so it |
michael@0 | 656 | is divided into two bandRects, one entirely above the band, and one containing |
michael@0 | 657 | the remainder of the rect. Insert the part that is totally above the |
michael@0 | 658 | bandRect before the current bandRect, as in case #1 above, and adjust the |
michael@0 | 659 | other band rect to exclude the part already added.</li> |
michael@0 | 660 | <li>case #5: the rect is totally below the current bandRect, so just |
michael@0 | 661 | skip to the next band</li> |
michael@0 | 662 | <li>case #3 and #4: rect is at least partially intersection with the |
michael@0 | 663 | bandRect, so divide the current band into two parts, where the top part is |
michael@0 | 664 | above the current rect. Move to the new band just created, which is |
michael@0 | 665 | the next band.</li> |
michael@0 | 666 | <li>case #6: the rect shares the bottom and height with the bandRect, |
michael@0 | 667 | so just add the rect to the band.</li> |
michael@0 | 668 | <li>case #4 and #7: create a new rect for the part that overlaps the |
michael@0 | 669 | bandRect, and add it to the current bandRect (similar to case #6) and then |
michael@0 | 670 | move on to the next band, removing that part from the rect passed in. If |
michael@0 | 671 | no more bands, append the rect passed in to the end of the bandRect list.</li> |
michael@0 | 672 | </ul> |
michael@0 | 673 | </ul> |
michael@0 | 674 | <i>This algorithm is pretty confusing - basically what needs to happen is |
michael@0 | 675 | that rects and bands need to be divided up so that complicated cases like |
michael@0 | 676 | #2, #4, and #7, are reduced to simpler cases where the rects is totally above, |
michael@0 | 677 | below, or between a band rect. From the current implementation, it |
michael@0 | 678 | might be worth verifying that the final result of the inserts is a correctly |
michael@0 | 679 | ordered liest of bandRects (debug mode only).</i> |
michael@0 | 680 | |
michael@0 | 681 | |
michael@0 | 682 | <h4>Algorithm 3: RemoveRegion</h4> |
michael@0 | 683 | When a float is removed, the Space Manager is notified by a call to RemoveRegion, |
michael@0 | 684 | passing in the frame that is being removed. |
michael@0 | 685 | |
michael@0 | 686 | <ul> |
michael@0 | 687 | <li>Get the FrameInfo for the frame passed in. If not found, an error is |
michael@0 | 688 | returned.</li> |
michael@0 | 689 | <li>If the rect for the frame is not empty, then visit each band in the |
michael@0 | 690 | bandList:</li> |
michael@0 | 691 | <ul> |
michael@0 | 692 | <li>for each rect in the band: |
michael@0 | 693 | |
michael@0 | 694 | </li> |
michael@0 | 695 | </ul> |
michael@0 | 696 | <ul> |
michael@0 | 697 | <ul> |
michael@0 | 698 | <li>if the bandRect is occupied by the frame, either remove the frame |
michael@0 | 699 | from the bandRect (if there are other frames sharing it) and remember that |
michael@0 | 700 | it was shared</li> |
michael@0 | 701 | <li>otherwise simply remove the bandRect (no other frames share it).</li> |
michael@0 | 702 | <li>if the bandRect was shared, then try to coalesce adjacent bandRects</li> |
michael@0 | 703 | <ul> |
michael@0 | 704 | <li>if the previous bandRect is directly next to the current bandRect, |
michael@0 | 705 | and they have the same frame list, then make the current bandRect cover the |
michael@0 | 706 | previous bandRect's full region (adjust the left edge to be that of the previous |
michael@0 | 707 | bandRect) and remove the previous bandRect.</li> |
michael@0 | 708 | </ul> |
michael@0 | 709 | </ul> |
michael@0 | 710 | </ul> |
michael@0 | 711 | <ul> |
michael@0 | 712 | <li>if the current band or prior band had a rect occupied byu the frame, |
michael@0 | 713 | then try to join the two bands via JoinBands</li> |
michael@0 | 714 | </ul> |
michael@0 | 715 | <li>Finally, destroy the frameInfo for the frame. |
michael@0 | 716 | |
michael@0 | 717 | </li> |
michael@0 | 718 | </ul> |
michael@0 | 719 | |
michael@0 | 720 | <br> |
michael@0 | 721 | |
michael@0 | 722 | <hr width="100%" size="2"> |
michael@0 | 723 | <h2>Cross-Component Algorithms</h2> |
michael@0 | 724 | |
michael@0 | 725 | <br> |
michael@0 | 726 | |
michael@0 | 727 | |
michael@0 | 728 | |
michael@0 | 729 | <hr width="100%" size="2"> |
michael@0 | 730 | <h2>Tech Notes</h2> |
michael@0 | 731 | <ul> |
michael@0 | 732 | <li> |
michael@0 | 733 | |
michael@0 | 734 | </li> |
michael@0 | 735 | |
michael@0 | 736 | </ul> |
michael@0 | 737 | |
michael@0 | 738 | |
michael@0 | 739 | |
michael@0 | 740 | |
michael@0 | 741 | |
michael@0 | 742 | </body> |
michael@0 | 743 | </html> |