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/. */
6 #include "nsListBoxLayout.h"
8 #include "nsListBoxBodyFrame.h"
10 #include "nsBoxLayoutState.h"
11 #include "nsIScrollableFrame.h"
12 #include "nsIReflowCallback.h"
13 #include "mozilla/dom/NameSpaceConstants.h"
14 #include "nsGkAtoms.h"
15 #include "nsContentUtils.h"
17 nsListBoxLayout::nsListBoxLayout() : nsGridRowGroupLayout()
21 ////////// nsBoxLayout //////////////
24 nsListBoxLayout::GetPrefSize(nsIFrame
* aBox
, nsBoxLayoutState
& aBoxLayoutState
)
26 nsSize pref
= nsGridRowGroupLayout::GetPrefSize(aBox
, aBoxLayoutState
);
28 nsListBoxBodyFrame
* frame
= static_cast<nsListBoxBodyFrame
*>(aBox
);
30 nscoord rowheight
= frame
->GetRowHeightAppUnits();
31 pref
.height
= frame
->GetRowCount() * rowheight
;
33 nscoord y
= frame
->GetAvailableHeight();
34 if (pref
.height
> y
&& y
> 0 && rowheight
> 0) {
35 nscoord m
= (pref
.height
-y
)%rowheight
;
36 nscoord remainder
= m
== 0 ? 0 : rowheight
- m
;
37 pref
.height
+= remainder
;
39 if (nsContentUtils::HasNonEmptyAttr(frame
->GetContent(), kNameSpaceID_None
,
40 nsGkAtoms::sizemode
)) {
41 nscoord width
= frame
->ComputeIntrinsicISize(aBoxLayoutState
);
42 if (width
> pref
.width
)
50 nsListBoxLayout::GetMinSize(nsIFrame
* aBox
, nsBoxLayoutState
& aBoxLayoutState
)
52 nsSize minSize
= nsGridRowGroupLayout::GetMinSize(aBox
, aBoxLayoutState
);
54 nsListBoxBodyFrame
* frame
= static_cast<nsListBoxBodyFrame
*>(aBox
);
56 nscoord rowheight
= frame
->GetRowHeightAppUnits();
57 minSize
.height
= frame
->GetRowCount() * rowheight
;
59 nscoord y
= frame
->GetAvailableHeight();
60 if (minSize
.height
> y
&& y
> 0 && rowheight
> 0) {
61 nscoord m
= (minSize
.height
-y
)%rowheight
;
62 nscoord remainder
= m
== 0 ? 0 : rowheight
- m
;
63 minSize
.height
+= remainder
;
65 if (nsContentUtils::HasNonEmptyAttr(frame
->GetContent(), kNameSpaceID_None
,
66 nsGkAtoms::sizemode
)) {
67 nscoord width
= frame
->ComputeIntrinsicISize(aBoxLayoutState
);
68 if (width
> minSize
.width
)
69 minSize
.width
= width
;
76 nsListBoxLayout::GetMaxSize(nsIFrame
* aBox
, nsBoxLayoutState
& aBoxLayoutState
)
78 nsSize maxSize
= nsGridRowGroupLayout::GetMaxSize(aBox
, aBoxLayoutState
);
80 nsListBoxBodyFrame
* frame
= static_cast<nsListBoxBodyFrame
*>(aBox
);
82 nscoord rowheight
= frame
->GetRowHeightAppUnits();
83 maxSize
.height
= frame
->GetRowCount() * rowheight
;
85 nscoord y
= frame
->GetAvailableHeight();
86 if (maxSize
.height
> y
&& y
> 0 && rowheight
> 0) {
87 nscoord m
= (maxSize
.height
-y
)%rowheight
;
88 nscoord remainder
= m
== 0 ? 0 : rowheight
- m
;
89 maxSize
.height
+= remainder
;
96 nsListBoxLayout::Layout(nsIFrame
* aBox
, nsBoxLayoutState
& aState
)
98 return LayoutInternal(aBox
, aState
);
102 /////////// nsListBoxLayout /////////////////////////
105 * Called to layout our our children. Does no frame construction
108 nsListBoxLayout::LayoutInternal(nsIFrame
* aBox
, nsBoxLayoutState
& aState
)
110 int32_t redrawStart
= -1;
112 // Get the start y position.
113 nsListBoxBodyFrame
* body
= static_cast<nsListBoxBodyFrame
*>(aBox
);
115 NS_ERROR("Frame encountered that isn't a listboxbody!");
116 return NS_ERROR_FAILURE
;
121 // Get our client rect.
123 aBox
->GetClientRect(clientRect
);
125 // Get the starting y position and the remaining available
127 nscoord availableHeight
= body
->GetAvailableHeight();
128 nscoord yOffset
= body
->GetYPosition();
130 if (availableHeight
<= 0) {
131 bool fixed
= (body
->GetFixedRowSize() != -1);
133 availableHeight
= 10;
138 // run through all our currently created children
139 nsIFrame
* box
= nsBox::GetChildBox(body
);
141 // if the reason is resize or initial we must relayout.
142 nscoord rowHeight
= body
->GetRowHeightAppUnits();
145 // If this box is dirty or if it has dirty children, we
146 // call layout on it.
147 nsRect
childRect(box
->GetRect());
148 box
->GetMargin(margin
);
150 // relayout if we must or we are dirty or some of our children are dirty
151 // or the client area is wider than us
152 // XXXldb There should probably be a resize check here too!
153 if (NS_SUBTREE_DIRTY(box
) || childRect
.width
< clientRect
.width
) {
155 childRect
.y
= yOffset
;
156 childRect
.width
= clientRect
.width
;
158 nsSize size
= box
->GetPrefSize(aState
);
159 body
->SetRowHeight(size
.height
);
161 childRect
.height
= rowHeight
;
163 childRect
.Deflate(margin
);
164 box
->SetBounds(aState
, childRect
);
167 // if the child did not need to be relayed out. Then its easy.
168 // Place the child by just grabbing its rect and adjusting the y.
169 int32_t newPos
= yOffset
+margin
.top
;
171 // are we pushing down or pulling up any rows?
172 // Then we may have to redraw everything below the moved
174 if (redrawStart
== -1 && childRect
.y
!= newPos
)
175 redrawStart
= newPos
;
177 childRect
.y
= newPos
;
178 box
->SetBounds(aState
, childRect
);
181 // Ok now the available size gets smaller and we move the
182 // starting position of the next child down some.
183 nscoord size
= childRect
.height
+ margin
.top
+ margin
.bottom
;
186 availableHeight
-= size
;
188 box
= nsBox::GetNextBox(box
);
191 // We have enough available height left to add some more rows
192 // Since we can't do this during layout, we post a callback
193 // that will be processed after the reflow completes.
194 body
->PostReflowCallback();
196 // if rows were pushed down or pulled up because some rows were added
197 // before them then redraw everything under the inserted rows. The inserted
198 // rows will automatically be redrawn because the were marked dirty on insertion.
199 if (redrawStart
> -1) {
200 aBox
->Redraw(aState
);
206 // Creation Routines ///////////////////////////////////////////////////////////////////////
208 already_AddRefed
<nsBoxLayout
> NS_NewListBoxLayout()
210 nsRefPtr
<nsBoxLayout
> layout
= new nsListBoxLayout();
211 return layout
.forget();