|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 // |
|
7 // Eric Vaughan |
|
8 // Netscape Communications |
|
9 // |
|
10 // See documentation in associated header file |
|
11 // |
|
12 |
|
13 #include "nsBoxLayoutState.h" |
|
14 #include "nsSprocketLayout.h" |
|
15 #include "nsPresContext.h" |
|
16 #include "nsCOMPtr.h" |
|
17 #include "nsIContent.h" |
|
18 #include "nsIPresShell.h" |
|
19 #include "nsContainerFrame.h" |
|
20 #include "nsBoxFrame.h" |
|
21 #include "StackArena.h" |
|
22 #include "mozilla/Likely.h" |
|
23 #include <algorithm> |
|
24 |
|
25 nsBoxLayout* nsSprocketLayout::gInstance = nullptr; |
|
26 |
|
27 //#define DEBUG_GROW |
|
28 |
|
29 #define DEBUG_SPRING_SIZE 8 |
|
30 #define DEBUG_BORDER_SIZE 2 |
|
31 #define COIL_SIZE 8 |
|
32 |
|
33 |
|
34 nsresult |
|
35 NS_NewSprocketLayout( nsIPresShell* aPresShell, nsCOMPtr<nsBoxLayout>& aNewLayout) |
|
36 { |
|
37 if (!nsSprocketLayout::gInstance) { |
|
38 nsSprocketLayout::gInstance = new nsSprocketLayout(); |
|
39 NS_IF_ADDREF(nsSprocketLayout::gInstance); |
|
40 } |
|
41 // we have not instance variables so just return our static one. |
|
42 aNewLayout = nsSprocketLayout::gInstance; |
|
43 return NS_OK; |
|
44 } |
|
45 |
|
46 /*static*/ void |
|
47 nsSprocketLayout::Shutdown() |
|
48 { |
|
49 NS_IF_RELEASE(gInstance); |
|
50 } |
|
51 |
|
52 nsSprocketLayout::nsSprocketLayout() |
|
53 { |
|
54 } |
|
55 |
|
56 bool |
|
57 nsSprocketLayout::IsHorizontal(nsIFrame* aBox) |
|
58 { |
|
59 return (aBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0; |
|
60 } |
|
61 |
|
62 void |
|
63 nsSprocketLayout::GetFrameState(nsIFrame* aBox, nsFrameState& aState) |
|
64 { |
|
65 aState = aBox->GetStateBits(); |
|
66 } |
|
67 |
|
68 static uint8_t |
|
69 GetFrameDirection(nsIFrame* aBox) |
|
70 { |
|
71 return aBox->StyleVisibility()->mDirection; |
|
72 } |
|
73 |
|
74 static void |
|
75 HandleBoxPack(nsIFrame* aBox, const nsFrameState& aFrameState, nscoord& aX, nscoord& aY, |
|
76 const nsRect& aOriginalRect, const nsRect& aClientRect) |
|
77 { |
|
78 // In the normal direction we lay out our kids in the positive direction (e.g., |x| will get |
|
79 // bigger for a horizontal box, and |y| will get bigger for a vertical box). In the reverse |
|
80 // direction, the opposite is true. We'll be laying out each child at a smaller |x| or |
|
81 // |y|. |
|
82 uint8_t frameDirection = GetFrameDirection(aBox); |
|
83 |
|
84 if (aFrameState & NS_STATE_IS_HORIZONTAL) { |
|
85 if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) { |
|
86 // The normal direction. |x| increases as we move through our children. |
|
87 aX = aClientRect.x; |
|
88 } |
|
89 else { |
|
90 // The reverse direction. |x| decreases as we move through our children. |
|
91 aX = aClientRect.x + aOriginalRect.width; |
|
92 } |
|
93 // |y| is always in the normal direction in horizontal boxes |
|
94 aY = aClientRect.y; |
|
95 } |
|
96 else { |
|
97 // take direction property into account for |x| in vertical boxes |
|
98 if (frameDirection == NS_STYLE_DIRECTION_LTR) { |
|
99 // The normal direction. |x| increases as we move through our children. |
|
100 aX = aClientRect.x; |
|
101 } |
|
102 else { |
|
103 // The reverse direction. |x| decreases as we move through our children. |
|
104 aX = aClientRect.x + aOriginalRect.width; |
|
105 } |
|
106 if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) { |
|
107 // The normal direction. |y| increases as we move through our children. |
|
108 aY = aClientRect.y; |
|
109 } |
|
110 else { |
|
111 // The reverse direction. |y| decreases as we move through our children. |
|
112 aY = aClientRect.y + aOriginalRect.height; |
|
113 } |
|
114 } |
|
115 |
|
116 // Get our pack/alignment information. |
|
117 nsIFrame::Halignment halign = aBox->GetHAlign(); |
|
118 nsIFrame::Valignment valign = aBox->GetVAlign(); |
|
119 |
|
120 // The following code handles box PACKING. Packing comes into play in the case where the computed size for |
|
121 // all of our children (now stored in our client rect) is smaller than the size available for |
|
122 // the box (stored in |aOriginalRect|). |
|
123 // |
|
124 // Here we adjust our |x| and |y| variables accordingly so that we start at the beginning, |
|
125 // middle, or end of the box. |
|
126 // |
|
127 // XXXdwh JUSTIFY needs to be implemented! |
|
128 if (aFrameState & NS_STATE_IS_HORIZONTAL) { |
|
129 switch(halign) { |
|
130 case nsBoxFrame::hAlign_Left: |
|
131 break; // Nothing to do. The default initialized us properly. |
|
132 |
|
133 case nsBoxFrame::hAlign_Center: |
|
134 if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
135 aX += (aOriginalRect.width - aClientRect.width)/2; |
|
136 else |
|
137 aX -= (aOriginalRect.width - aClientRect.width)/2; |
|
138 break; |
|
139 |
|
140 case nsBoxFrame::hAlign_Right: |
|
141 if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
142 aX += (aOriginalRect.width - aClientRect.width); |
|
143 else |
|
144 aX -= (aOriginalRect.width - aClientRect.width); |
|
145 break; // Nothing to do for the reverse dir. The default initialized us properly. |
|
146 } |
|
147 } else { |
|
148 switch(valign) { |
|
149 case nsBoxFrame::vAlign_Top: |
|
150 case nsBoxFrame::vAlign_BaseLine: // This value is technically impossible to specify for pack. |
|
151 break; // Don't do anything. We were initialized correctly. |
|
152 |
|
153 case nsBoxFrame::vAlign_Middle: |
|
154 if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
155 aY += (aOriginalRect.height - aClientRect.height)/2; |
|
156 else |
|
157 aY -= (aOriginalRect.height - aClientRect.height)/2; |
|
158 break; |
|
159 |
|
160 case nsBoxFrame::vAlign_Bottom: |
|
161 if (aFrameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
162 aY += (aOriginalRect.height - aClientRect.height); |
|
163 else |
|
164 aY -= (aOriginalRect.height - aClientRect.height); |
|
165 break; |
|
166 } |
|
167 } |
|
168 } |
|
169 |
|
170 NS_IMETHODIMP |
|
171 nsSprocketLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState) |
|
172 { |
|
173 // See if we are collapsed. If we are, then simply iterate over all our |
|
174 // children and give them a rect of 0 width and height. |
|
175 if (aBox->IsCollapsed()) { |
|
176 nsIFrame* child = aBox->GetChildBox(); |
|
177 while(child) |
|
178 { |
|
179 nsBoxFrame::LayoutChildAt(aState, child, nsRect(0,0,0,0)); |
|
180 child = child->GetNextBox(); |
|
181 } |
|
182 return NS_OK; |
|
183 } |
|
184 |
|
185 nsBoxLayoutState::AutoReflowDepth depth(aState); |
|
186 mozilla::AutoStackArena arena; |
|
187 |
|
188 // ----- figure out our size ---------- |
|
189 const nsSize originalSize = aBox->GetSize(); |
|
190 |
|
191 // -- make sure we remove our border and padding ---- |
|
192 nsRect clientRect; |
|
193 aBox->GetClientRect(clientRect); |
|
194 |
|
195 // |originalClientRect| represents the rect of the entire box (excluding borders |
|
196 // and padding). We store it here because we're going to use |clientRect| to hold |
|
197 // the required size for all our kids. As an example, consider an hbox with a |
|
198 // specified width of 300. If the kids total only 150 pixels of width, then |
|
199 // we have 150 pixels left over. |clientRect| is going to hold a width of 150 and |
|
200 // is going to be adjusted based off the value of the PACK property. If flexible |
|
201 // objects are in the box, then the two rects will match. |
|
202 nsRect originalClientRect(clientRect); |
|
203 |
|
204 // The frame state contains cached knowledge about our box, such as our orientation |
|
205 // and direction. |
|
206 nsFrameState frameState = nsFrameState(0); |
|
207 GetFrameState(aBox, frameState); |
|
208 |
|
209 // Build a list of our children's desired sizes and computed sizes |
|
210 nsBoxSize* boxSizes = nullptr; |
|
211 nsComputedBoxSize* computedBoxSizes = nullptr; |
|
212 |
|
213 nscoord min = 0; |
|
214 nscoord max = 0; |
|
215 int32_t flexes = 0; |
|
216 PopulateBoxSizes(aBox, aState, boxSizes, min, max, flexes); |
|
217 |
|
218 // The |size| variable will hold the total size of children along the axis of |
|
219 // the box. Continuing with the example begun in the comment above, size would |
|
220 // be 150 pixels. |
|
221 nscoord size = clientRect.width; |
|
222 if (!IsHorizontal(aBox)) |
|
223 size = clientRect.height; |
|
224 ComputeChildSizes(aBox, aState, size, boxSizes, computedBoxSizes); |
|
225 |
|
226 // After the call to ComputeChildSizes, the |size| variable contains the |
|
227 // total required size of all the children. We adjust our clientRect in the |
|
228 // appropriate dimension to match this size. In our example, we now assign |
|
229 // 150 pixels into the clientRect.width. |
|
230 // |
|
231 // The variables |min| and |max| hold the minimum required size box must be |
|
232 // in the OPPOSITE orientation, e.g., for a horizontal box, |min| is the minimum |
|
233 // height we require to enclose our children, and |max| is the maximum height |
|
234 // required to enclose our children. |
|
235 if (IsHorizontal(aBox)) { |
|
236 clientRect.width = size; |
|
237 if (clientRect.height < min) |
|
238 clientRect.height = min; |
|
239 |
|
240 if (frameState & NS_STATE_AUTO_STRETCH) { |
|
241 if (clientRect.height > max) |
|
242 clientRect.height = max; |
|
243 } |
|
244 } else { |
|
245 clientRect.height = size; |
|
246 if (clientRect.width < min) |
|
247 clientRect.width = min; |
|
248 |
|
249 if (frameState & NS_STATE_AUTO_STRETCH) { |
|
250 if (clientRect.width > max) |
|
251 clientRect.width = max; |
|
252 } |
|
253 } |
|
254 |
|
255 // With the sizes computed, now it's time to lay out our children. |
|
256 bool finished; |
|
257 nscoord passes = 0; |
|
258 |
|
259 // We flow children at their preferred locations (along with the appropriate computed flex). |
|
260 // After we flow a child, it is possible that the child will change its size. If/when this happens, |
|
261 // we have to do another pass. Typically only 2 passes are required, but the code is prepared to |
|
262 // do as many passes as are necessary to achieve equilibrium. |
|
263 nscoord x = 0; |
|
264 nscoord y = 0; |
|
265 nscoord origX = 0; |
|
266 nscoord origY = 0; |
|
267 |
|
268 // |childResized| lets us know if a child changed its size after we attempted to lay it out at |
|
269 // the specified size. If this happens, we usually have to do another pass. |
|
270 bool childResized = false; |
|
271 |
|
272 // |passes| stores our number of passes. If for any reason we end up doing more than, say, 10 |
|
273 // passes, we assert to indicate that something is seriously screwed up. |
|
274 passes = 0; |
|
275 do |
|
276 { |
|
277 #ifdef DEBUG_REFLOW |
|
278 if (passes > 0) { |
|
279 AddIndents(); |
|
280 printf("ChildResized doing pass: %d\n", passes); |
|
281 } |
|
282 #endif |
|
283 |
|
284 // Always assume that we're done. This will change if, for example, children don't stay |
|
285 // the same size after being flowed. |
|
286 finished = true; |
|
287 |
|
288 // Handle box packing. |
|
289 HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect); |
|
290 |
|
291 // Now that packing is taken care of we set up a few additional |
|
292 // tracking variables. |
|
293 origX = x; |
|
294 origY = y; |
|
295 |
|
296 nscoord nextX = x; |
|
297 nscoord nextY = y; |
|
298 |
|
299 // Now we iterate over our box children and our box size lists in |
|
300 // parallel. For each child, we look at its sizes and figure out |
|
301 // where to place it. |
|
302 nsComputedBoxSize* childComputedBoxSize = computedBoxSizes; |
|
303 nsBoxSize* childBoxSize = boxSizes; |
|
304 |
|
305 nsIFrame* child = aBox->GetChildBox(); |
|
306 |
|
307 int32_t count = 0; |
|
308 while (child || (childBoxSize && childBoxSize->bogus)) |
|
309 { |
|
310 // If for some reason, our lists are not the same length, we guard |
|
311 // by bailing out of the loop. |
|
312 if (childBoxSize == nullptr) { |
|
313 NS_NOTREACHED("Lists not the same length."); |
|
314 break; |
|
315 } |
|
316 |
|
317 nscoord width = clientRect.width; |
|
318 nscoord height = clientRect.height; |
|
319 |
|
320 if (!childBoxSize->bogus) { |
|
321 // We have a valid box size entry. This entry already contains information about our |
|
322 // sizes along the axis of the box (e.g., widths in a horizontal box). If our default |
|
323 // ALIGN is not stretch, however, then we also need to know the child's size along the |
|
324 // opposite axis. |
|
325 if (!(frameState & NS_STATE_AUTO_STRETCH)) { |
|
326 nsSize prefSize = child->GetPrefSize(aState); |
|
327 nsSize minSize = child->GetMinSize(aState); |
|
328 nsSize maxSize = child->GetMaxSize(aState); |
|
329 prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize); |
|
330 |
|
331 AddMargin(child, prefSize); |
|
332 width = std::min(prefSize.width, originalClientRect.width); |
|
333 height = std::min(prefSize.height, originalClientRect.height); |
|
334 } |
|
335 } |
|
336 |
|
337 // Obtain the computed size along the axis of the box for this child from the computedBoxSize entry. |
|
338 // We store the result in |width| for horizontal boxes and |height| for vertical boxes. |
|
339 if (frameState & NS_STATE_IS_HORIZONTAL) |
|
340 width = childComputedBoxSize->size; |
|
341 else |
|
342 height = childComputedBoxSize->size; |
|
343 |
|
344 // Adjust our x/y for the left/right spacing. |
|
345 if (frameState & NS_STATE_IS_HORIZONTAL) { |
|
346 if (frameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
347 x += (childBoxSize->left); |
|
348 else |
|
349 x -= (childBoxSize->right); |
|
350 } else { |
|
351 if (frameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
352 y += (childBoxSize->left); |
|
353 else |
|
354 y -= (childBoxSize->right); |
|
355 } |
|
356 |
|
357 nextX = x; |
|
358 nextY = y; |
|
359 |
|
360 // Now we build a child rect. |
|
361 nscoord rectX = x; |
|
362 nscoord rectY = y; |
|
363 if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) { |
|
364 if (frameState & NS_STATE_IS_HORIZONTAL) |
|
365 rectX -= width; |
|
366 else |
|
367 rectY -= height; |
|
368 } |
|
369 |
|
370 // We now create an accurate child rect based off our computed size information. |
|
371 nsRect childRect(rectX, rectY, width, height); |
|
372 |
|
373 // Sanity check against our clientRect. It is possible that a child specified |
|
374 // a size that is too large to fit. If that happens, then we have to grow |
|
375 // our client rect. Remember, clientRect is not the total rect of the enclosing |
|
376 // box. It currently holds our perception of how big the children needed to |
|
377 // be. |
|
378 if (childRect.width > clientRect.width) |
|
379 clientRect.width = childRect.width; |
|
380 |
|
381 if (childRect.height > clientRect.height) |
|
382 clientRect.height = childRect.height; |
|
383 |
|
384 // Either |nextX| or |nextY| is updated by this function call, according |
|
385 // to our axis. |
|
386 ComputeChildsNextPosition(aBox, x, y, nextX, nextY, childRect); |
|
387 |
|
388 // Now we further update our nextX/Y along our axis. |
|
389 // We also set childRect.y/x along the opposite axis appropriately for a |
|
390 // stretch alignment. (Non-stretch alignment is handled below.) |
|
391 if (frameState & NS_STATE_IS_HORIZONTAL) { |
|
392 if (frameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
393 nextX += (childBoxSize->right); |
|
394 else |
|
395 nextX -= (childBoxSize->left); |
|
396 childRect.y = originalClientRect.y; |
|
397 } |
|
398 else { |
|
399 if (frameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
400 nextY += (childBoxSize->right); |
|
401 else |
|
402 nextY -= (childBoxSize->left); |
|
403 childRect.x = originalClientRect.x; |
|
404 } |
|
405 |
|
406 // If we encounter a completely bogus box size, we just leave this child completely |
|
407 // alone and continue through the loop to the next child. |
|
408 if (childBoxSize->bogus) |
|
409 { |
|
410 childComputedBoxSize = childComputedBoxSize->next; |
|
411 childBoxSize = childBoxSize->next; |
|
412 count++; |
|
413 x = nextX; |
|
414 y = nextY; |
|
415 continue; |
|
416 } |
|
417 |
|
418 nsMargin margin(0,0,0,0); |
|
419 |
|
420 bool layout = true; |
|
421 |
|
422 // Deflate the rect of our child by its margin. |
|
423 child->GetMargin(margin); |
|
424 childRect.Deflate(margin); |
|
425 if (childRect.width < 0) |
|
426 childRect.width = 0; |
|
427 if (childRect.height < 0) |
|
428 childRect.height = 0; |
|
429 |
|
430 // Now we're trying to figure out if we have to lay out this child, i.e., to call |
|
431 // the child's Layout method. |
|
432 if (passes > 0) { |
|
433 layout = false; |
|
434 } else { |
|
435 // Always perform layout if we are dirty or have dirty children |
|
436 if (!NS_SUBTREE_DIRTY(child)) |
|
437 layout = false; |
|
438 } |
|
439 |
|
440 nsRect oldRect(child->GetRect()); |
|
441 |
|
442 // Non-stretch alignment will be handled in AlignChildren(), so don't |
|
443 // change child out-of-axis positions yet. |
|
444 if (!(frameState & NS_STATE_AUTO_STRETCH)) { |
|
445 if (frameState & NS_STATE_IS_HORIZONTAL) { |
|
446 childRect.y = oldRect.y; |
|
447 } else { |
|
448 childRect.x = oldRect.x; |
|
449 } |
|
450 } |
|
451 |
|
452 // We computed a childRect. Now we want to set the bounds of the child to be that rect. |
|
453 // If our old rect is different, then we know our size changed and we cache that fact |
|
454 // in the |sizeChanged| variable. |
|
455 |
|
456 child->SetBounds(aState, childRect); |
|
457 bool sizeChanged = (childRect.width != oldRect.width || |
|
458 childRect.height != oldRect.height); |
|
459 |
|
460 if (sizeChanged) { |
|
461 // Our size is different. Sanity check against our maximum allowed size to ensure |
|
462 // we didn't exceed it. |
|
463 nsSize minSize = child->GetMinSize(aState); |
|
464 nsSize maxSize = child->GetMaxSize(aState); |
|
465 maxSize = nsBox::BoundsCheckMinMax(minSize, maxSize); |
|
466 |
|
467 // make sure the size is in our max size. |
|
468 if (childRect.width > maxSize.width) |
|
469 childRect.width = maxSize.width; |
|
470 |
|
471 if (childRect.height > maxSize.height) |
|
472 childRect.height = maxSize.height; |
|
473 |
|
474 // set it again |
|
475 child->SetBounds(aState, childRect); |
|
476 } |
|
477 |
|
478 // If we already determined that layout was required or if our size has changed, then |
|
479 // we make sure to call layout on the child, since its children may need to be shifted |
|
480 // around as a result of the size change. |
|
481 if (layout || sizeChanged) |
|
482 child->Layout(aState); |
|
483 |
|
484 // If the child was a block or inline (e.g., HTML) it may have changed its rect *during* layout. |
|
485 // We have to check for this. |
|
486 nsRect newChildRect(child->GetRect()); |
|
487 |
|
488 if (!newChildRect.IsEqualInterior(childRect)) { |
|
489 #ifdef DEBUG_GROW |
|
490 child->DumpBox(stdout); |
|
491 printf(" GREW from (%d,%d) -> (%d,%d)\n", childRect.width, childRect.height, newChildRect.width, newChildRect.height); |
|
492 #endif |
|
493 newChildRect.Inflate(margin); |
|
494 childRect.Inflate(margin); |
|
495 |
|
496 // The child changed size during layout. The ChildResized method handles this |
|
497 // scenario. |
|
498 ChildResized(aBox, |
|
499 aState, |
|
500 child, |
|
501 childBoxSize, |
|
502 childComputedBoxSize, |
|
503 boxSizes, |
|
504 computedBoxSizes, |
|
505 childRect, |
|
506 newChildRect, |
|
507 clientRect, |
|
508 flexes, |
|
509 finished); |
|
510 |
|
511 // We note that a child changed size, which means that another pass will be required. |
|
512 childResized = true; |
|
513 |
|
514 // Now that a child resized, it's entirely possible that OUR rect is too small. Now we |
|
515 // ensure that |originalClientRect| is grown to accommodate the size of |clientRect|. |
|
516 if (clientRect.width > originalClientRect.width) |
|
517 originalClientRect.width = clientRect.width; |
|
518 |
|
519 if (clientRect.height > originalClientRect.height) |
|
520 originalClientRect.height = clientRect.height; |
|
521 |
|
522 if (!(frameState & NS_STATE_IS_DIRECTION_NORMAL)) { |
|
523 // Our childRect had its XMost() or YMost() (depending on our layout |
|
524 // direction), positioned at a certain point. Ensure that the |
|
525 // newChildRect satisfies the same constraint. Note that this is |
|
526 // just equivalent to adjusting the x/y by the difference in |
|
527 // width/height between childRect and newChildRect. So we don't need |
|
528 // to reaccount for the left and right of the box layout state again. |
|
529 if (frameState & NS_STATE_IS_HORIZONTAL) |
|
530 newChildRect.x = childRect.XMost() - newChildRect.width; |
|
531 else |
|
532 newChildRect.y = childRect.YMost() - newChildRect.height; |
|
533 } |
|
534 |
|
535 // If the child resized then recompute its position. |
|
536 ComputeChildsNextPosition(aBox, x, y, nextX, nextY, newChildRect); |
|
537 |
|
538 if (newChildRect.width >= margin.left + margin.right && newChildRect.height >= margin.top + margin.bottom) |
|
539 newChildRect.Deflate(margin); |
|
540 |
|
541 if (childRect.width >= margin.left + margin.right && childRect.height >= margin.top + margin.bottom) |
|
542 childRect.Deflate(margin); |
|
543 |
|
544 child->SetBounds(aState, newChildRect); |
|
545 |
|
546 // If we are the first box that changed size, then we don't need to do a second pass |
|
547 if (count == 0) |
|
548 finished = true; |
|
549 } |
|
550 |
|
551 // Now update our x/y finally. |
|
552 x = nextX; |
|
553 y = nextY; |
|
554 |
|
555 // Move to the next child. |
|
556 childComputedBoxSize = childComputedBoxSize->next; |
|
557 childBoxSize = childBoxSize->next; |
|
558 |
|
559 child = child->GetNextBox(); |
|
560 count++; |
|
561 } |
|
562 |
|
563 // Sanity-checking code to ensure we don't do an infinite # of passes. |
|
564 passes++; |
|
565 NS_ASSERTION(passes < 10, "A Box's child is constantly growing!!!!!"); |
|
566 if (passes > 10) |
|
567 break; |
|
568 } while (false == finished); |
|
569 |
|
570 // Get rid of our size lists. |
|
571 while(boxSizes) |
|
572 { |
|
573 nsBoxSize* toDelete = boxSizes; |
|
574 boxSizes = boxSizes->next; |
|
575 delete toDelete; |
|
576 } |
|
577 |
|
578 while(computedBoxSizes) |
|
579 { |
|
580 nsComputedBoxSize* toDelete = computedBoxSizes; |
|
581 computedBoxSizes = computedBoxSizes->next; |
|
582 delete toDelete; |
|
583 } |
|
584 |
|
585 if (childResized) { |
|
586 // See if one of our children forced us to get bigger |
|
587 nsRect tmpClientRect(originalClientRect); |
|
588 nsMargin bp(0,0,0,0); |
|
589 aBox->GetBorderAndPadding(bp); |
|
590 tmpClientRect.Inflate(bp); |
|
591 |
|
592 if (tmpClientRect.width > originalSize.width || tmpClientRect.height > originalSize.height) |
|
593 { |
|
594 // if it did reset our bounds. |
|
595 nsRect bounds(aBox->GetRect()); |
|
596 if (tmpClientRect.width > originalSize.width) |
|
597 bounds.width = tmpClientRect.width; |
|
598 |
|
599 if (tmpClientRect.height > originalSize.height) |
|
600 bounds.height = tmpClientRect.height; |
|
601 |
|
602 aBox->SetBounds(aState, bounds); |
|
603 } |
|
604 } |
|
605 |
|
606 // Because our size grew, we now have to readjust because of box packing. Repack |
|
607 // in order to update our x and y to the correct values. |
|
608 HandleBoxPack(aBox, frameState, x, y, originalClientRect, clientRect); |
|
609 |
|
610 // Compare against our original x and y and only worry about adjusting the children if |
|
611 // we really did have to change the positions because of packing (typically for 'center' |
|
612 // or 'end' pack values). |
|
613 if (x != origX || y != origY) { |
|
614 nsIFrame* child = aBox->GetChildBox(); |
|
615 |
|
616 // reposition all our children |
|
617 while (child) |
|
618 { |
|
619 nsRect childRect(child->GetRect()); |
|
620 childRect.x += (x - origX); |
|
621 childRect.y += (y - origY); |
|
622 child->SetBounds(aState, childRect); |
|
623 child = child->GetNextBox(); |
|
624 } |
|
625 } |
|
626 |
|
627 // Perform out-of-axis alignment for non-stretch alignments |
|
628 if (!(frameState & NS_STATE_AUTO_STRETCH)) { |
|
629 AlignChildren(aBox, aState); |
|
630 } |
|
631 |
|
632 // That's it! If you made it this far without having a nervous breakdown, |
|
633 // congratulations! Go get yourself a beer. |
|
634 return NS_OK; |
|
635 } |
|
636 |
|
637 void |
|
638 nsSprocketLayout::PopulateBoxSizes(nsIFrame* aBox, nsBoxLayoutState& aState, nsBoxSize*& aBoxSizes, nscoord& aMinSize, nscoord& aMaxSize, int32_t& aFlexes) |
|
639 { |
|
640 // used for the equal size flag |
|
641 nscoord biggestPrefWidth = 0; |
|
642 nscoord biggestMinWidth = 0; |
|
643 nscoord smallestMaxWidth = NS_INTRINSICSIZE; |
|
644 |
|
645 nsFrameState frameState = nsFrameState(0); |
|
646 GetFrameState(aBox, frameState); |
|
647 |
|
648 //if (frameState & NS_STATE_CURRENTLY_IN_DEBUG) |
|
649 // printf("In debug\n"); |
|
650 |
|
651 aMinSize = 0; |
|
652 aMaxSize = NS_INTRINSICSIZE; |
|
653 |
|
654 bool isHorizontal; |
|
655 |
|
656 if (IsHorizontal(aBox)) |
|
657 isHorizontal = true; |
|
658 else |
|
659 isHorizontal = false; |
|
660 |
|
661 // this is a nice little optimization |
|
662 // it turns out that if we only have 1 flexable child |
|
663 // then it does not matter what its preferred size is |
|
664 // there is nothing to flex it relative. This is great |
|
665 // because we can avoid asking for a preferred size in this |
|
666 // case. Why is this good? Well you might have html inside it |
|
667 // and asking html for its preferred size is rather expensive. |
|
668 // so we can just optimize it out this way. |
|
669 |
|
670 // set flexes |
|
671 nsIFrame* child = aBox->GetChildBox(); |
|
672 |
|
673 aFlexes = 0; |
|
674 nsBoxSize* currentBox = nullptr; |
|
675 |
|
676 #if 0 |
|
677 nsBoxSize* start = aBoxSizes; |
|
678 |
|
679 while(child) |
|
680 { |
|
681 // ok if we started with a list move down the list |
|
682 // until we reach the end. Then start looking at childen. |
|
683 // This feature is used extensively for Grid. |
|
684 nscoord flex = 0; |
|
685 |
|
686 if (!start) { |
|
687 if (!currentBox) { |
|
688 aBoxSizes = new (aState) nsBoxSize(); |
|
689 currentBox = aBoxSizes; |
|
690 } else { |
|
691 currentBox->next = new (aState) nsBoxSize(); |
|
692 currentBox = currentBox->next; |
|
693 } |
|
694 |
|
695 |
|
696 flex = child->GetFlex(aState); |
|
697 |
|
698 currentBox->flex = flex; |
|
699 currentBox->collapsed = child->IsCollapsed(); |
|
700 } else { |
|
701 flex = start->flex; |
|
702 start = start->next; |
|
703 } |
|
704 |
|
705 if (flex > 0) |
|
706 aFlexes++; |
|
707 |
|
708 child = child->GetNextBox(); |
|
709 } |
|
710 #endif |
|
711 |
|
712 // get pref, min, max |
|
713 child = aBox->GetChildBox(); |
|
714 currentBox = aBoxSizes; |
|
715 nsBoxSize* last = nullptr; |
|
716 |
|
717 nscoord maxFlex = 0; |
|
718 int32_t childCount = 0; |
|
719 |
|
720 while(child) |
|
721 { |
|
722 while (currentBox && currentBox->bogus) { |
|
723 last = currentBox; |
|
724 currentBox = currentBox->next; |
|
725 } |
|
726 ++childCount; |
|
727 nsSize pref(0,0); |
|
728 nsSize minSize(0,0); |
|
729 nsSize maxSize(NS_INTRINSICSIZE,NS_INTRINSICSIZE); |
|
730 nscoord ascent = 0; |
|
731 bool collapsed = child->IsCollapsed(); |
|
732 |
|
733 if (!collapsed) { |
|
734 // only one flexible child? Cool we will just make its preferred size |
|
735 // 0 then and not even have to ask for it. |
|
736 //if (flexes != 1) { |
|
737 |
|
738 pref = child->GetPrefSize(aState); |
|
739 minSize = child->GetMinSize(aState); |
|
740 maxSize = nsBox::BoundsCheckMinMax(minSize, child->GetMaxSize(aState)); |
|
741 ascent = child->GetBoxAscent(aState); |
|
742 nsMargin margin; |
|
743 child->GetMargin(margin); |
|
744 ascent += margin.top; |
|
745 //} |
|
746 |
|
747 pref = nsBox::BoundsCheck(minSize, pref, maxSize); |
|
748 |
|
749 AddMargin(child, pref); |
|
750 AddMargin(child, minSize); |
|
751 AddMargin(child, maxSize); |
|
752 } |
|
753 |
|
754 if (!currentBox) { |
|
755 // create one. |
|
756 currentBox = new (aState) nsBoxSize(); |
|
757 if (!aBoxSizes) { |
|
758 aBoxSizes = currentBox; |
|
759 last = aBoxSizes; |
|
760 } else { |
|
761 last->next = currentBox; |
|
762 last = currentBox; |
|
763 } |
|
764 |
|
765 nscoord minWidth; |
|
766 nscoord maxWidth; |
|
767 nscoord prefWidth; |
|
768 |
|
769 // get sizes from child |
|
770 if (isHorizontal) { |
|
771 minWidth = minSize.width; |
|
772 maxWidth = maxSize.width; |
|
773 prefWidth = pref.width; |
|
774 } else { |
|
775 minWidth = minSize.height; |
|
776 maxWidth = maxSize.height; |
|
777 prefWidth = pref.height; |
|
778 } |
|
779 |
|
780 nscoord flex = child->GetFlex(aState); |
|
781 |
|
782 // set them if you collapsed you are not flexible. |
|
783 if (collapsed) { |
|
784 currentBox->flex = 0; |
|
785 } |
|
786 else { |
|
787 if (flex > maxFlex) { |
|
788 maxFlex = flex; |
|
789 } |
|
790 currentBox->flex = flex; |
|
791 } |
|
792 |
|
793 // we specified all our children are equal size; |
|
794 if (frameState & NS_STATE_EQUAL_SIZE) { |
|
795 |
|
796 if (prefWidth > biggestPrefWidth) |
|
797 biggestPrefWidth = prefWidth; |
|
798 |
|
799 if (minWidth > biggestMinWidth) |
|
800 biggestMinWidth = minWidth; |
|
801 |
|
802 if (maxWidth < smallestMaxWidth) |
|
803 smallestMaxWidth = maxWidth; |
|
804 } else { // not we can set our children right now. |
|
805 currentBox->pref = prefWidth; |
|
806 currentBox->min = minWidth; |
|
807 currentBox->max = maxWidth; |
|
808 } |
|
809 |
|
810 NS_ASSERTION(minWidth <= prefWidth && prefWidth <= maxWidth,"Bad min, pref, max widths!"); |
|
811 |
|
812 } |
|
813 |
|
814 if (!isHorizontal) { |
|
815 if (minSize.width > aMinSize) |
|
816 aMinSize = minSize.width; |
|
817 |
|
818 if (maxSize.width < aMaxSize) |
|
819 aMaxSize = maxSize.width; |
|
820 |
|
821 } else { |
|
822 if (minSize.height > aMinSize) |
|
823 aMinSize = minSize.height; |
|
824 |
|
825 if (maxSize.height < aMaxSize) |
|
826 aMaxSize = maxSize.height; |
|
827 } |
|
828 |
|
829 currentBox->collapsed = collapsed; |
|
830 aFlexes += currentBox->flex; |
|
831 |
|
832 child = child->GetNextBox(); |
|
833 |
|
834 last = currentBox; |
|
835 currentBox = currentBox->next; |
|
836 |
|
837 } |
|
838 |
|
839 if (childCount > 0) { |
|
840 nscoord maxAllowedFlex = nscoord_MAX / childCount; |
|
841 |
|
842 if (MOZ_UNLIKELY(maxFlex > maxAllowedFlex)) { |
|
843 // clamp all the flexes |
|
844 currentBox = aBoxSizes; |
|
845 while (currentBox) { |
|
846 currentBox->flex = std::min(currentBox->flex, maxAllowedFlex); |
|
847 currentBox = currentBox->next; |
|
848 } |
|
849 } |
|
850 } |
|
851 #ifdef DEBUG |
|
852 else { |
|
853 NS_ASSERTION(maxFlex == 0, "How did that happen?"); |
|
854 } |
|
855 #endif |
|
856 |
|
857 // we specified all our children are equal size; |
|
858 if (frameState & NS_STATE_EQUAL_SIZE) { |
|
859 smallestMaxWidth = std::max(smallestMaxWidth, biggestMinWidth); |
|
860 biggestPrefWidth = nsBox::BoundsCheck(biggestMinWidth, biggestPrefWidth, smallestMaxWidth); |
|
861 |
|
862 currentBox = aBoxSizes; |
|
863 |
|
864 while(currentBox) |
|
865 { |
|
866 if (!currentBox->collapsed) { |
|
867 currentBox->pref = biggestPrefWidth; |
|
868 currentBox->min = biggestMinWidth; |
|
869 currentBox->max = smallestMaxWidth; |
|
870 } else { |
|
871 currentBox->pref = 0; |
|
872 currentBox->min = 0; |
|
873 currentBox->max = 0; |
|
874 } |
|
875 currentBox = currentBox->next; |
|
876 } |
|
877 } |
|
878 |
|
879 } |
|
880 |
|
881 void |
|
882 nsSprocketLayout::ComputeChildsNextPosition(nsIFrame* aBox, |
|
883 const nscoord& aCurX, |
|
884 const nscoord& aCurY, |
|
885 nscoord& aNextX, |
|
886 nscoord& aNextY, |
|
887 const nsRect& aCurrentChildSize) |
|
888 { |
|
889 // Get the position along the box axis for the child. |
|
890 // The out-of-axis position is not set. |
|
891 nsFrameState frameState = nsFrameState(0); |
|
892 GetFrameState(aBox, frameState); |
|
893 |
|
894 if (IsHorizontal(aBox)) { |
|
895 // horizontal box's children. |
|
896 if (frameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
897 aNextX = aCurX + aCurrentChildSize.width; |
|
898 else |
|
899 aNextX = aCurX - aCurrentChildSize.width; |
|
900 |
|
901 } else { |
|
902 // vertical box's children. |
|
903 if (frameState & NS_STATE_IS_DIRECTION_NORMAL) |
|
904 aNextY = aCurY + aCurrentChildSize.height; |
|
905 else |
|
906 aNextY = aCurY - aCurrentChildSize.height; |
|
907 } |
|
908 } |
|
909 |
|
910 void |
|
911 nsSprocketLayout::AlignChildren(nsIFrame* aBox, |
|
912 nsBoxLayoutState& aState) |
|
913 { |
|
914 nsFrameState frameState = nsFrameState(0); |
|
915 GetFrameState(aBox, frameState); |
|
916 bool isHorizontal = (frameState & NS_STATE_IS_HORIZONTAL) != 0; |
|
917 nsRect clientRect; |
|
918 aBox->GetClientRect(clientRect); |
|
919 |
|
920 NS_PRECONDITION(!(frameState & NS_STATE_AUTO_STRETCH), |
|
921 "Only AlignChildren() with non-stretch alignment"); |
|
922 |
|
923 // These are only calculated if needed |
|
924 nsIFrame::Halignment halign; |
|
925 nsIFrame::Valignment valign; |
|
926 nscoord maxAscent; |
|
927 bool isLTR; |
|
928 |
|
929 if (isHorizontal) { |
|
930 valign = aBox->GetVAlign(); |
|
931 if (valign == nsBoxFrame::vAlign_BaseLine) { |
|
932 maxAscent = aBox->GetBoxAscent(aState); |
|
933 } |
|
934 } else { |
|
935 isLTR = GetFrameDirection(aBox) == NS_STYLE_DIRECTION_LTR; |
|
936 halign = aBox->GetHAlign(); |
|
937 } |
|
938 |
|
939 nsIFrame* child = aBox->GetChildBox(); |
|
940 while (child) { |
|
941 |
|
942 nsMargin margin; |
|
943 child->GetMargin(margin); |
|
944 nsRect childRect = child->GetRect(); |
|
945 |
|
946 if (isHorizontal) { |
|
947 const nscoord startAlign = clientRect.y + margin.top; |
|
948 const nscoord endAlign = |
|
949 clientRect.YMost() - margin.bottom - childRect.height; |
|
950 |
|
951 nscoord y; |
|
952 switch (valign) { |
|
953 case nsBoxFrame::vAlign_Top: |
|
954 y = startAlign; |
|
955 break; |
|
956 case nsBoxFrame::vAlign_Middle: |
|
957 // Should this center the border box? |
|
958 // This centers the margin box, the historical behavior. |
|
959 y = (startAlign + endAlign) / 2; |
|
960 break; |
|
961 case nsBoxFrame::vAlign_Bottom: |
|
962 y = endAlign; |
|
963 break; |
|
964 case nsBoxFrame::vAlign_BaseLine: |
|
965 // Alignments don't force the box to grow (only sizes do), |
|
966 // so keep the children within the box. |
|
967 y = maxAscent - child->GetBoxAscent(aState); |
|
968 y = std::max(startAlign, y); |
|
969 y = std::min(y, endAlign); |
|
970 break; |
|
971 } |
|
972 |
|
973 childRect.y = y; |
|
974 |
|
975 } else { // vertical box |
|
976 const nscoord leftAlign = clientRect.x + margin.left; |
|
977 const nscoord rightAlign = |
|
978 clientRect.XMost() - margin.right - childRect.width; |
|
979 |
|
980 nscoord x; |
|
981 switch (halign) { |
|
982 case nsBoxFrame::hAlign_Left: // start |
|
983 x = isLTR ? leftAlign : rightAlign; |
|
984 break; |
|
985 case nsBoxFrame::hAlign_Center: |
|
986 x = (leftAlign + rightAlign) / 2; |
|
987 break; |
|
988 case nsBoxFrame::hAlign_Right: // end |
|
989 x = isLTR ? rightAlign : leftAlign; |
|
990 break; |
|
991 } |
|
992 |
|
993 childRect.x = x; |
|
994 } |
|
995 |
|
996 if (childRect.TopLeft() != child->GetPosition()) { |
|
997 child->SetBounds(aState, childRect); |
|
998 } |
|
999 |
|
1000 child = child->GetNextBox(); |
|
1001 } |
|
1002 } |
|
1003 |
|
1004 void |
|
1005 nsSprocketLayout::ChildResized(nsIFrame* aBox, |
|
1006 nsBoxLayoutState& aState, |
|
1007 nsIFrame* aChild, |
|
1008 nsBoxSize* aChildBoxSize, |
|
1009 nsComputedBoxSize* aChildComputedSize, |
|
1010 nsBoxSize* aBoxSizes, |
|
1011 nsComputedBoxSize* aComputedBoxSizes, |
|
1012 const nsRect& aChildLayoutRect, |
|
1013 nsRect& aChildActualRect, |
|
1014 nsRect& aContainingRect, |
|
1015 int32_t aFlexes, |
|
1016 bool& aFinished) |
|
1017 |
|
1018 { |
|
1019 nsRect childCurrentRect(aChildLayoutRect); |
|
1020 |
|
1021 bool isHorizontal = IsHorizontal(aBox); |
|
1022 nscoord childLayoutWidth = GET_WIDTH(aChildLayoutRect,isHorizontal); |
|
1023 nscoord& childActualWidth = GET_WIDTH(aChildActualRect,isHorizontal); |
|
1024 nscoord& containingWidth = GET_WIDTH(aContainingRect,isHorizontal); |
|
1025 |
|
1026 //nscoord childLayoutHeight = GET_HEIGHT(aChildLayoutRect,isHorizontal); |
|
1027 nscoord& childActualHeight = GET_HEIGHT(aChildActualRect,isHorizontal); |
|
1028 nscoord& containingHeight = GET_HEIGHT(aContainingRect,isHorizontal); |
|
1029 |
|
1030 bool recompute = false; |
|
1031 |
|
1032 // if we are a horizontal box see if the child will fit inside us. |
|
1033 if ( childActualHeight > containingHeight) { |
|
1034 // if we are a horizontal box and the child is bigger than our height |
|
1035 |
|
1036 // ok if the height changed then we need to reflow everyone but us at the new height |
|
1037 // so we will set the changed index to be us. And signal that we need a new pass. |
|
1038 |
|
1039 nsSize min = aChild->GetMinSize(aState); |
|
1040 nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState)); |
|
1041 AddMargin(aChild, max); |
|
1042 |
|
1043 if (isHorizontal) |
|
1044 childActualHeight = max.height < childActualHeight ? max.height : childActualHeight; |
|
1045 else |
|
1046 childActualHeight = max.width < childActualHeight ? max.width : childActualHeight; |
|
1047 |
|
1048 // only set if it changes |
|
1049 if (childActualHeight > containingHeight) { |
|
1050 containingHeight = childActualHeight; |
|
1051 |
|
1052 // remember we do not need to clear the resized list because changing the height of a horizontal box |
|
1053 // will not affect the width of any of its children because block flow left to right, top to bottom. Just trust me |
|
1054 // on this one. |
|
1055 aFinished = false; |
|
1056 |
|
1057 // only recompute if there are flexes. |
|
1058 if (aFlexes > 0) { |
|
1059 // relayout everything |
|
1060 recompute = true; |
|
1061 InvalidateComputedSizes(aComputedBoxSizes); |
|
1062 nsComputedBoxSize* node = aComputedBoxSizes; |
|
1063 |
|
1064 while(node) { |
|
1065 node->resized = false; |
|
1066 node = node->next; |
|
1067 } |
|
1068 |
|
1069 } |
|
1070 } |
|
1071 } |
|
1072 |
|
1073 if (childActualWidth > childLayoutWidth) { |
|
1074 nsSize min = aChild->GetMinSize(aState); |
|
1075 nsSize max = nsBox::BoundsCheckMinMax(min, aChild->GetMaxSize(aState)); |
|
1076 |
|
1077 AddMargin(aChild, max); |
|
1078 |
|
1079 // our width now becomes the new size |
|
1080 |
|
1081 if (isHorizontal) |
|
1082 childActualWidth = max.width < childActualWidth ? max.width : childActualWidth; |
|
1083 else |
|
1084 childActualWidth = max.height < childActualWidth ? max.height : childActualWidth; |
|
1085 |
|
1086 if (childActualWidth > childLayoutWidth) { |
|
1087 aChildComputedSize->size = childActualWidth; |
|
1088 aChildBoxSize->min = childActualWidth; |
|
1089 if (aChildBoxSize->pref < childActualWidth) |
|
1090 aChildBoxSize->pref = childActualWidth; |
|
1091 if (aChildBoxSize->max < childActualWidth) |
|
1092 aChildBoxSize->max = childActualWidth; |
|
1093 |
|
1094 // if we have flexible elements with us then reflex things. Otherwise we can skip doing it. |
|
1095 if (aFlexes > 0) { |
|
1096 InvalidateComputedSizes(aComputedBoxSizes); |
|
1097 |
|
1098 nsComputedBoxSize* node = aComputedBoxSizes; |
|
1099 aChildComputedSize->resized = true; |
|
1100 |
|
1101 while(node) { |
|
1102 if (node->resized) |
|
1103 node->valid = true; |
|
1104 |
|
1105 node = node->next; |
|
1106 } |
|
1107 |
|
1108 recompute = true; |
|
1109 aFinished = false; |
|
1110 } else { |
|
1111 containingWidth += aChildComputedSize->size - childLayoutWidth; |
|
1112 } |
|
1113 } |
|
1114 } |
|
1115 |
|
1116 if (recompute) |
|
1117 ComputeChildSizes(aBox, aState, containingWidth, aBoxSizes, aComputedBoxSizes); |
|
1118 |
|
1119 if (!childCurrentRect.IsEqualInterior(aChildActualRect)) { |
|
1120 // the childRect includes the margin |
|
1121 // make sure we remove it before setting |
|
1122 // the bounds. |
|
1123 nsMargin margin(0,0,0,0); |
|
1124 aChild->GetMargin(margin); |
|
1125 nsRect rect(aChildActualRect); |
|
1126 if (rect.width >= margin.left + margin.right && rect.height >= margin.top + margin.bottom) |
|
1127 rect.Deflate(margin); |
|
1128 |
|
1129 aChild->SetBounds(aState, rect); |
|
1130 aChild->Layout(aState); |
|
1131 } |
|
1132 |
|
1133 } |
|
1134 |
|
1135 void |
|
1136 nsSprocketLayout::InvalidateComputedSizes(nsComputedBoxSize* aComputedBoxSizes) |
|
1137 { |
|
1138 while(aComputedBoxSizes) { |
|
1139 aComputedBoxSizes->valid = false; |
|
1140 aComputedBoxSizes = aComputedBoxSizes->next; |
|
1141 } |
|
1142 } |
|
1143 |
|
1144 void |
|
1145 nsSprocketLayout::ComputeChildSizes(nsIFrame* aBox, |
|
1146 nsBoxLayoutState& aState, |
|
1147 nscoord& aGivenSize, |
|
1148 nsBoxSize* aBoxSizes, |
|
1149 nsComputedBoxSize*& aComputedBoxSizes) |
|
1150 { |
|
1151 |
|
1152 //nscoord onePixel = aState.PresContext()->IntScaledPixelsToTwips(1); |
|
1153 |
|
1154 int32_t sizeRemaining = aGivenSize; |
|
1155 int32_t spacerConstantsRemaining = 0; |
|
1156 |
|
1157 // ----- calculate the spacers constants and the size remaining ----- |
|
1158 |
|
1159 if (!aComputedBoxSizes) |
|
1160 aComputedBoxSizes = new (aState) nsComputedBoxSize(); |
|
1161 |
|
1162 nsBoxSize* boxSizes = aBoxSizes; |
|
1163 nsComputedBoxSize* computedBoxSizes = aComputedBoxSizes; |
|
1164 int32_t count = 0; |
|
1165 int32_t validCount = 0; |
|
1166 |
|
1167 while (boxSizes) |
|
1168 { |
|
1169 |
|
1170 NS_ASSERTION((boxSizes->min <= boxSizes->pref && boxSizes->pref <= boxSizes->max),"bad pref, min, max size"); |
|
1171 |
|
1172 |
|
1173 // ignore collapsed children |
|
1174 // if (boxSizes->collapsed) |
|
1175 // { |
|
1176 // computedBoxSizes->valid = true; |
|
1177 // computedBoxSizes->size = boxSizes->pref; |
|
1178 // validCount++; |
|
1179 // boxSizes->flex = 0; |
|
1180 // }// else { |
|
1181 |
|
1182 if (computedBoxSizes->valid) { |
|
1183 sizeRemaining -= computedBoxSizes->size; |
|
1184 validCount++; |
|
1185 } else { |
|
1186 if (boxSizes->flex == 0) |
|
1187 { |
|
1188 computedBoxSizes->valid = true; |
|
1189 computedBoxSizes->size = boxSizes->pref; |
|
1190 validCount++; |
|
1191 } |
|
1192 |
|
1193 spacerConstantsRemaining += boxSizes->flex; |
|
1194 sizeRemaining -= boxSizes->pref; |
|
1195 } |
|
1196 |
|
1197 sizeRemaining -= (boxSizes->left + boxSizes->right); |
|
1198 |
|
1199 //} |
|
1200 |
|
1201 boxSizes = boxSizes->next; |
|
1202 |
|
1203 if (boxSizes && !computedBoxSizes->next) |
|
1204 computedBoxSizes->next = new (aState) nsComputedBoxSize(); |
|
1205 |
|
1206 computedBoxSizes = computedBoxSizes->next; |
|
1207 count++; |
|
1208 } |
|
1209 |
|
1210 // everything accounted for? |
|
1211 if (validCount < count) |
|
1212 { |
|
1213 // ----- Ok we are give a size to fit into so stretch or squeeze to fit |
|
1214 // ----- Make sure we look at our min and max size |
|
1215 bool limit = true; |
|
1216 for (int pass=1; true == limit; pass++) |
|
1217 { |
|
1218 limit = false; |
|
1219 boxSizes = aBoxSizes; |
|
1220 computedBoxSizes = aComputedBoxSizes; |
|
1221 |
|
1222 while (boxSizes) { |
|
1223 |
|
1224 // ignore collapsed spacers |
|
1225 |
|
1226 // if (!boxSizes->collapsed) { |
|
1227 |
|
1228 nscoord pref = 0; |
|
1229 nscoord max = NS_INTRINSICSIZE; |
|
1230 nscoord min = 0; |
|
1231 nscoord flex = 0; |
|
1232 |
|
1233 pref = boxSizes->pref; |
|
1234 min = boxSizes->min; |
|
1235 max = boxSizes->max; |
|
1236 flex = boxSizes->flex; |
|
1237 |
|
1238 // ----- look at our min and max limits make sure we aren't too small or too big ----- |
|
1239 if (!computedBoxSizes->valid) { |
|
1240 int32_t newSize = pref + int32_t(int64_t(sizeRemaining) * flex / spacerConstantsRemaining); |
|
1241 |
|
1242 if (newSize<=min) { |
|
1243 computedBoxSizes->size = min; |
|
1244 computedBoxSizes->valid = true; |
|
1245 spacerConstantsRemaining -= flex; |
|
1246 sizeRemaining += pref; |
|
1247 sizeRemaining -= min; |
|
1248 limit = true; |
|
1249 } else if (newSize>=max) { |
|
1250 computedBoxSizes->size = max; |
|
1251 computedBoxSizes->valid = true; |
|
1252 spacerConstantsRemaining -= flex; |
|
1253 sizeRemaining += pref; |
|
1254 sizeRemaining -= max; |
|
1255 limit = true; |
|
1256 } |
|
1257 } |
|
1258 // } |
|
1259 boxSizes = boxSizes->next; |
|
1260 computedBoxSizes = computedBoxSizes->next; |
|
1261 } |
|
1262 } |
|
1263 } |
|
1264 |
|
1265 // ---- once we have removed and min and max issues just stretch us out in the remaining space |
|
1266 // ---- or shrink us. Depends on the size remaining and the spacer constants |
|
1267 aGivenSize = 0; |
|
1268 boxSizes = aBoxSizes; |
|
1269 computedBoxSizes = aComputedBoxSizes; |
|
1270 |
|
1271 while (boxSizes) { |
|
1272 |
|
1273 // ignore collapsed spacers |
|
1274 // if (!(boxSizes && boxSizes->collapsed)) { |
|
1275 |
|
1276 nscoord pref = 0; |
|
1277 nscoord flex = 0; |
|
1278 pref = boxSizes->pref; |
|
1279 flex = boxSizes->flex; |
|
1280 |
|
1281 if (!computedBoxSizes->valid) { |
|
1282 computedBoxSizes->size = pref + int32_t(int64_t(sizeRemaining) * flex / spacerConstantsRemaining); |
|
1283 computedBoxSizes->valid = true; |
|
1284 } |
|
1285 |
|
1286 aGivenSize += (boxSizes->left + boxSizes->right); |
|
1287 aGivenSize += computedBoxSizes->size; |
|
1288 |
|
1289 // } |
|
1290 |
|
1291 boxSizes = boxSizes->next; |
|
1292 computedBoxSizes = computedBoxSizes->next; |
|
1293 } |
|
1294 } |
|
1295 |
|
1296 |
|
1297 nsSize |
|
1298 nsSprocketLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState) |
|
1299 { |
|
1300 nsSize vpref (0, 0); |
|
1301 bool isHorizontal = IsHorizontal(aBox); |
|
1302 |
|
1303 nscoord biggestPref = 0; |
|
1304 |
|
1305 // run through all the children and get their min, max, and preferred sizes |
|
1306 // return us the size of the box |
|
1307 |
|
1308 nsIFrame* child = aBox->GetChildBox(); |
|
1309 nsFrameState frameState = nsFrameState(0); |
|
1310 GetFrameState(aBox, frameState); |
|
1311 bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE); |
|
1312 int32_t count = 0; |
|
1313 |
|
1314 while (child) |
|
1315 { |
|
1316 // ignore collapsed children |
|
1317 if (!child->IsCollapsed()) |
|
1318 { |
|
1319 nsSize pref = child->GetPrefSize(aState); |
|
1320 AddMargin(child, pref); |
|
1321 |
|
1322 if (isEqual) { |
|
1323 if (isHorizontal) |
|
1324 { |
|
1325 if (pref.width > biggestPref) |
|
1326 biggestPref = pref.width; |
|
1327 } else { |
|
1328 if (pref.height > biggestPref) |
|
1329 biggestPref = pref.height; |
|
1330 } |
|
1331 } |
|
1332 |
|
1333 AddLargestSize(vpref, pref, isHorizontal); |
|
1334 count++; |
|
1335 } |
|
1336 |
|
1337 child = child->GetNextBox(); |
|
1338 } |
|
1339 |
|
1340 if (isEqual) { |
|
1341 if (isHorizontal) |
|
1342 vpref.width = biggestPref*count; |
|
1343 else |
|
1344 vpref.height = biggestPref*count; |
|
1345 } |
|
1346 |
|
1347 // now add our border and padding |
|
1348 AddBorderAndPadding(aBox, vpref); |
|
1349 |
|
1350 return vpref; |
|
1351 } |
|
1352 |
|
1353 nsSize |
|
1354 nsSprocketLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aState) |
|
1355 { |
|
1356 nsSize minSize (0, 0); |
|
1357 bool isHorizontal = IsHorizontal(aBox); |
|
1358 |
|
1359 nscoord biggestMin = 0; |
|
1360 |
|
1361 |
|
1362 // run through all the children and get their min, max, and preferred sizes |
|
1363 // return us the size of the box |
|
1364 |
|
1365 nsIFrame* child = aBox->GetChildBox(); |
|
1366 nsFrameState frameState = nsFrameState(0); |
|
1367 GetFrameState(aBox, frameState); |
|
1368 bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE); |
|
1369 int32_t count = 0; |
|
1370 |
|
1371 while (child) |
|
1372 { |
|
1373 // ignore collapsed children |
|
1374 if (!child->IsCollapsed()) |
|
1375 { |
|
1376 nsSize min = child->GetMinSize(aState); |
|
1377 nsSize pref(0,0); |
|
1378 |
|
1379 // if the child is not flexible then |
|
1380 // its min size is its pref size. |
|
1381 if (child->GetFlex(aState) == 0) { |
|
1382 pref = child->GetPrefSize(aState); |
|
1383 if (isHorizontal) |
|
1384 min.width = pref.width; |
|
1385 else |
|
1386 min.height = pref.height; |
|
1387 } |
|
1388 |
|
1389 if (isEqual) { |
|
1390 if (isHorizontal) |
|
1391 { |
|
1392 if (min.width > biggestMin) |
|
1393 biggestMin = min.width; |
|
1394 } else { |
|
1395 if (min.height > biggestMin) |
|
1396 biggestMin = min.height; |
|
1397 } |
|
1398 } |
|
1399 |
|
1400 AddMargin(child, min); |
|
1401 AddLargestSize(minSize, min, isHorizontal); |
|
1402 count++; |
|
1403 } |
|
1404 |
|
1405 child = child->GetNextBox(); |
|
1406 } |
|
1407 |
|
1408 |
|
1409 if (isEqual) { |
|
1410 if (isHorizontal) |
|
1411 minSize.width = biggestMin*count; |
|
1412 else |
|
1413 minSize.height = biggestMin*count; |
|
1414 } |
|
1415 |
|
1416 // now add our border and padding |
|
1417 AddBorderAndPadding(aBox, minSize); |
|
1418 |
|
1419 return minSize; |
|
1420 } |
|
1421 |
|
1422 nsSize |
|
1423 nsSprocketLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState) |
|
1424 { |
|
1425 |
|
1426 bool isHorizontal = IsHorizontal(aBox); |
|
1427 |
|
1428 nscoord smallestMax = NS_INTRINSICSIZE; |
|
1429 nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE); |
|
1430 |
|
1431 // run through all the children and get their min, max, and preferred sizes |
|
1432 // return us the size of the box |
|
1433 |
|
1434 nsIFrame* child = aBox->GetChildBox(); |
|
1435 nsFrameState frameState = nsFrameState(0); |
|
1436 GetFrameState(aBox, frameState); |
|
1437 bool isEqual = !!(frameState & NS_STATE_EQUAL_SIZE); |
|
1438 int32_t count = 0; |
|
1439 |
|
1440 while (child) |
|
1441 { |
|
1442 // ignore collapsed children |
|
1443 if (!child->IsCollapsed()) |
|
1444 { |
|
1445 // if completely redefined don't even ask our child for its size. |
|
1446 nsSize min = child->GetMinSize(aState); |
|
1447 nsSize max = nsBox::BoundsCheckMinMax(min, child->GetMaxSize(aState)); |
|
1448 |
|
1449 AddMargin(child, max); |
|
1450 AddSmallestSize(maxSize, max, isHorizontal); |
|
1451 |
|
1452 if (isEqual) { |
|
1453 if (isHorizontal) |
|
1454 { |
|
1455 if (max.width < smallestMax) |
|
1456 smallestMax = max.width; |
|
1457 } else { |
|
1458 if (max.height < smallestMax) |
|
1459 smallestMax = max.height; |
|
1460 } |
|
1461 } |
|
1462 count++; |
|
1463 } |
|
1464 |
|
1465 child = child->GetNextBox(); |
|
1466 } |
|
1467 |
|
1468 if (isEqual) { |
|
1469 if (isHorizontal) { |
|
1470 if (smallestMax != NS_INTRINSICSIZE) |
|
1471 maxSize.width = smallestMax*count; |
|
1472 else |
|
1473 maxSize.width = NS_INTRINSICSIZE; |
|
1474 } else { |
|
1475 if (smallestMax != NS_INTRINSICSIZE) |
|
1476 maxSize.height = smallestMax*count; |
|
1477 else |
|
1478 maxSize.height = NS_INTRINSICSIZE; |
|
1479 } |
|
1480 } |
|
1481 |
|
1482 // now add our border and padding |
|
1483 AddBorderAndPadding(aBox, maxSize); |
|
1484 |
|
1485 return maxSize; |
|
1486 } |
|
1487 |
|
1488 |
|
1489 nscoord |
|
1490 nsSprocketLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState) |
|
1491 { |
|
1492 nscoord vAscent = 0; |
|
1493 |
|
1494 bool isHorizontal = IsHorizontal(aBox); |
|
1495 |
|
1496 // run through all the children and get their min, max, and preferred sizes |
|
1497 // return us the size of the box |
|
1498 |
|
1499 nsIFrame* child = aBox->GetChildBox(); |
|
1500 |
|
1501 while (child) |
|
1502 { |
|
1503 // ignore collapsed children |
|
1504 //if (!child->IsCollapsed()) |
|
1505 //{ |
|
1506 // if completely redefined don't even ask our child for its size. |
|
1507 nscoord ascent = child->GetBoxAscent(aState); |
|
1508 |
|
1509 nsMargin margin; |
|
1510 child->GetMargin(margin); |
|
1511 ascent += margin.top; |
|
1512 |
|
1513 if (isHorizontal) |
|
1514 { |
|
1515 if (ascent > vAscent) |
|
1516 vAscent = ascent; |
|
1517 } else { |
|
1518 if (vAscent == 0) |
|
1519 vAscent = ascent; |
|
1520 } |
|
1521 //} |
|
1522 |
|
1523 child = child->GetNextBox(); |
|
1524 } |
|
1525 |
|
1526 nsMargin borderPadding; |
|
1527 aBox->GetBorderAndPadding(borderPadding); |
|
1528 |
|
1529 return vAscent + borderPadding.top; |
|
1530 } |
|
1531 |
|
1532 void |
|
1533 nsSprocketLayout::SetLargestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal) |
|
1534 { |
|
1535 if (aIsHorizontal) |
|
1536 { |
|
1537 if (aSize1.height < aSize2.height) |
|
1538 aSize1.height = aSize2.height; |
|
1539 } else { |
|
1540 if (aSize1.width < aSize2.width) |
|
1541 aSize1.width = aSize2.width; |
|
1542 } |
|
1543 } |
|
1544 |
|
1545 void |
|
1546 nsSprocketLayout::SetSmallestSize(nsSize& aSize1, const nsSize& aSize2, bool aIsHorizontal) |
|
1547 { |
|
1548 if (aIsHorizontal) |
|
1549 { |
|
1550 if (aSize1.height > aSize2.height) |
|
1551 aSize1.height = aSize2.height; |
|
1552 } else { |
|
1553 if (aSize1.width > aSize2.width) |
|
1554 aSize1.width = aSize2.width; |
|
1555 |
|
1556 } |
|
1557 } |
|
1558 |
|
1559 void |
|
1560 nsSprocketLayout::AddLargestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal) |
|
1561 { |
|
1562 if (aIsHorizontal) |
|
1563 AddCoord(aSize.width, aSizeToAdd.width); |
|
1564 else |
|
1565 AddCoord(aSize.height, aSizeToAdd.height); |
|
1566 |
|
1567 SetLargestSize(aSize, aSizeToAdd, aIsHorizontal); |
|
1568 } |
|
1569 |
|
1570 void |
|
1571 nsSprocketLayout::AddCoord(nscoord& aCoord, nscoord aCoordToAdd) |
|
1572 { |
|
1573 if (aCoord != NS_INTRINSICSIZE) |
|
1574 { |
|
1575 if (aCoordToAdd == NS_INTRINSICSIZE) |
|
1576 aCoord = aCoordToAdd; |
|
1577 else |
|
1578 aCoord += aCoordToAdd; |
|
1579 } |
|
1580 } |
|
1581 void |
|
1582 nsSprocketLayout::AddSmallestSize(nsSize& aSize, const nsSize& aSizeToAdd, bool aIsHorizontal) |
|
1583 { |
|
1584 if (aIsHorizontal) |
|
1585 AddCoord(aSize.width, aSizeToAdd.width); |
|
1586 else |
|
1587 AddCoord(aSize.height, aSizeToAdd.height); |
|
1588 |
|
1589 SetSmallestSize(aSize, aSizeToAdd, aIsHorizontal); |
|
1590 } |
|
1591 |
|
1592 bool |
|
1593 nsSprocketLayout::GetDefaultFlex(int32_t& aFlex) |
|
1594 { |
|
1595 aFlex = 0; |
|
1596 return true; |
|
1597 } |
|
1598 |
|
1599 nsComputedBoxSize::nsComputedBoxSize() |
|
1600 { |
|
1601 resized = false; |
|
1602 valid = false; |
|
1603 size = 0; |
|
1604 next = nullptr; |
|
1605 } |
|
1606 |
|
1607 nsBoxSize::nsBoxSize() |
|
1608 { |
|
1609 pref = 0; |
|
1610 min = 0; |
|
1611 max = NS_INTRINSICSIZE; |
|
1612 collapsed = false; |
|
1613 left = 0; |
|
1614 right = 0; |
|
1615 flex = 0; |
|
1616 next = nullptr; |
|
1617 bogus = false; |
|
1618 } |
|
1619 |
|
1620 |
|
1621 void* |
|
1622 nsBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW |
|
1623 { |
|
1624 return mozilla::AutoStackArena::Allocate(sz); |
|
1625 } |
|
1626 |
|
1627 |
|
1628 void |
|
1629 nsBoxSize::operator delete(void* aPtr, size_t sz) |
|
1630 { |
|
1631 } |
|
1632 |
|
1633 |
|
1634 void* |
|
1635 nsComputedBoxSize::operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW |
|
1636 { |
|
1637 return mozilla::AutoStackArena::Allocate(sz); |
|
1638 } |
|
1639 |
|
1640 void |
|
1641 nsComputedBoxSize::operator delete(void* aPtr, size_t sz) |
|
1642 { |
|
1643 } |