Line data Source code
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 : #include "nsListBoxLayout.h"
7 :
8 : #include "nsListBoxBodyFrame.h"
9 : #include "nsBox.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"
16 :
17 0 : nsListBoxLayout::nsListBoxLayout() : nsGridRowGroupLayout()
18 : {
19 0 : }
20 :
21 : ////////// nsBoxLayout //////////////
22 :
23 : nsSize
24 0 : nsListBoxLayout::GetXULPrefSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState)
25 : {
26 0 : nsSize pref = nsGridRowGroupLayout::GetXULPrefSize(aBox, aBoxLayoutState);
27 :
28 0 : nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
29 0 : if (frame) {
30 0 : nscoord rowheight = frame->GetRowHeightAppUnits();
31 0 : pref.height = frame->GetRowCount() * rowheight;
32 : // Pad the height.
33 0 : nscoord y = frame->GetAvailableHeight();
34 0 : if (pref.height > y && y > 0 && rowheight > 0) {
35 0 : nscoord m = (pref.height-y)%rowheight;
36 0 : nscoord remainder = m == 0 ? 0 : rowheight - m;
37 0 : pref.height += remainder;
38 : }
39 0 : if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
40 : nsGkAtoms::sizemode)) {
41 0 : nscoord width = frame->ComputeIntrinsicISize(aBoxLayoutState);
42 0 : if (width > pref.width)
43 0 : pref.width = width;
44 : }
45 : }
46 0 : return pref;
47 : }
48 :
49 : nsSize
50 0 : nsListBoxLayout::GetXULMinSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState)
51 : {
52 0 : nsSize minSize = nsGridRowGroupLayout::GetXULMinSize(aBox, aBoxLayoutState);
53 :
54 0 : nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
55 0 : if (frame) {
56 0 : nscoord rowheight = frame->GetRowHeightAppUnits();
57 0 : minSize.height = frame->GetRowCount() * rowheight;
58 : // Pad the height.
59 0 : nscoord y = frame->GetAvailableHeight();
60 0 : if (minSize.height > y && y > 0 && rowheight > 0) {
61 0 : nscoord m = (minSize.height-y)%rowheight;
62 0 : nscoord remainder = m == 0 ? 0 : rowheight - m;
63 0 : minSize.height += remainder;
64 : }
65 0 : if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
66 : nsGkAtoms::sizemode)) {
67 0 : nscoord width = frame->ComputeIntrinsicISize(aBoxLayoutState);
68 0 : if (width > minSize.width)
69 0 : minSize.width = width;
70 : }
71 : }
72 0 : return minSize;
73 : }
74 :
75 : nsSize
76 0 : nsListBoxLayout::GetXULMaxSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState)
77 : {
78 0 : nsSize maxSize = nsGridRowGroupLayout::GetXULMaxSize(aBox, aBoxLayoutState);
79 :
80 0 : nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
81 0 : if (frame) {
82 0 : nscoord rowheight = frame->GetRowHeightAppUnits();
83 0 : maxSize.height = frame->GetRowCount() * rowheight;
84 : // Pad the height.
85 0 : nscoord y = frame->GetAvailableHeight();
86 0 : if (maxSize.height > y && y > 0 && rowheight > 0) {
87 0 : nscoord m = (maxSize.height-y)%rowheight;
88 0 : nscoord remainder = m == 0 ? 0 : rowheight - m;
89 0 : maxSize.height += remainder;
90 : }
91 : }
92 0 : return maxSize;
93 : }
94 :
95 : NS_IMETHODIMP
96 0 : nsListBoxLayout::XULLayout(nsIFrame* aBox, nsBoxLayoutState& aState)
97 : {
98 0 : return LayoutInternal(aBox, aState);
99 : }
100 :
101 :
102 : /////////// nsListBoxLayout /////////////////////////
103 :
104 : /**
105 : * Called to layout our our children. Does no frame construction
106 : */
107 : NS_IMETHODIMP
108 0 : nsListBoxLayout::LayoutInternal(nsIFrame* aBox, nsBoxLayoutState& aState)
109 : {
110 0 : int32_t redrawStart = -1;
111 :
112 : // Get the start y position.
113 0 : nsListBoxBodyFrame* body = static_cast<nsListBoxBodyFrame*>(aBox);
114 0 : if (!body) {
115 0 : NS_ERROR("Frame encountered that isn't a listboxbody!");
116 0 : return NS_ERROR_FAILURE;
117 : }
118 :
119 0 : nsMargin margin;
120 :
121 : // Get our client rect.
122 0 : nsRect clientRect;
123 0 : aBox->GetXULClientRect(clientRect);
124 :
125 : // Get the starting y position and the remaining available
126 : // height.
127 0 : nscoord availableHeight = body->GetAvailableHeight();
128 0 : nscoord yOffset = body->GetYPosition();
129 :
130 0 : if (availableHeight <= 0) {
131 0 : bool fixed = (body->GetFixedRowSize() != -1);
132 0 : if (fixed)
133 0 : availableHeight = 10;
134 : else
135 0 : return NS_OK;
136 : }
137 :
138 : // run through all our currently created children
139 0 : nsIFrame* box = nsBox::GetChildXULBox(body);
140 :
141 : // if the reason is resize or initial we must relayout.
142 0 : nscoord rowHeight = body->GetRowHeightAppUnits();
143 :
144 0 : while (box) {
145 : // If this box is dirty or if it has dirty children, we
146 : // call layout on it.
147 0 : nsRect childRect(box->GetRect());
148 0 : box->GetXULMargin(margin);
149 :
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 0 : if (NS_SUBTREE_DIRTY(box) || childRect.width < clientRect.width) {
154 0 : childRect.x = 0;
155 0 : childRect.y = yOffset;
156 0 : childRect.width = clientRect.width;
157 :
158 0 : nsSize size = box->GetXULPrefSize(aState);
159 0 : body->SetRowHeight(size.height);
160 :
161 0 : childRect.height = rowHeight;
162 :
163 0 : childRect.Deflate(margin);
164 0 : box->SetXULBounds(aState, childRect);
165 0 : box->XULLayout(aState);
166 : } else {
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 0 : int32_t newPos = yOffset+margin.top;
170 :
171 : // are we pushing down or pulling up any rows?
172 : // Then we may have to redraw everything below the moved
173 : // rows.
174 0 : if (redrawStart == -1 && childRect.y != newPos)
175 0 : redrawStart = newPos;
176 :
177 0 : childRect.y = newPos;
178 0 : box->SetXULBounds(aState, childRect);
179 : }
180 :
181 : // Ok now the available size gets smaller and we move the
182 : // starting position of the next child down some.
183 0 : nscoord size = childRect.height + margin.top + margin.bottom;
184 :
185 0 : yOffset += size;
186 0 : availableHeight -= size;
187 :
188 0 : box = nsBox::GetNextXULBox(box);
189 : }
190 :
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 0 : body->PostReflowCallback();
195 :
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 0 : if (redrawStart > -1) {
200 0 : aBox->XULRedraw(aState);
201 : }
202 :
203 0 : return NS_OK;
204 : }
205 :
206 : // Creation Routines ///////////////////////////////////////////////////////////////////////
207 :
208 0 : already_AddRefed<nsBoxLayout> NS_NewListBoxLayout()
209 : {
210 0 : RefPtr<nsBoxLayout> layout = new nsListBoxLayout();
211 0 : return layout.forget();
212 : }
|