Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "nsListBoxLayout.h" |
michael@0 | 7 | |
michael@0 | 8 | #include "nsListBoxBodyFrame.h" |
michael@0 | 9 | #include "nsBox.h" |
michael@0 | 10 | #include "nsBoxLayoutState.h" |
michael@0 | 11 | #include "nsIScrollableFrame.h" |
michael@0 | 12 | #include "nsIReflowCallback.h" |
michael@0 | 13 | #include "nsNameSpaceManager.h" |
michael@0 | 14 | #include "nsGkAtoms.h" |
michael@0 | 15 | #include "nsContentUtils.h" |
michael@0 | 16 | |
michael@0 | 17 | nsListBoxLayout::nsListBoxLayout() : nsGridRowGroupLayout() |
michael@0 | 18 | { |
michael@0 | 19 | } |
michael@0 | 20 | |
michael@0 | 21 | ////////// nsBoxLayout ////////////// |
michael@0 | 22 | |
michael@0 | 23 | nsSize |
michael@0 | 24 | nsListBoxLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState) |
michael@0 | 25 | { |
michael@0 | 26 | nsSize pref = nsGridRowGroupLayout::GetPrefSize(aBox, aBoxLayoutState); |
michael@0 | 27 | |
michael@0 | 28 | nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox); |
michael@0 | 29 | if (frame) { |
michael@0 | 30 | nscoord rowheight = frame->GetRowHeightAppUnits(); |
michael@0 | 31 | pref.height = frame->GetRowCount() * rowheight; |
michael@0 | 32 | // Pad the height. |
michael@0 | 33 | nscoord y = frame->GetAvailableHeight(); |
michael@0 | 34 | if (pref.height > y && y > 0 && rowheight > 0) { |
michael@0 | 35 | nscoord m = (pref.height-y)%rowheight; |
michael@0 | 36 | nscoord remainder = m == 0 ? 0 : rowheight - m; |
michael@0 | 37 | pref.height += remainder; |
michael@0 | 38 | } |
michael@0 | 39 | if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None, |
michael@0 | 40 | nsGkAtoms::sizemode)) { |
michael@0 | 41 | nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState); |
michael@0 | 42 | if (width > pref.width) |
michael@0 | 43 | pref.width = width; |
michael@0 | 44 | } |
michael@0 | 45 | } |
michael@0 | 46 | return pref; |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | nsSize |
michael@0 | 50 | nsListBoxLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState) |
michael@0 | 51 | { |
michael@0 | 52 | nsSize minSize = nsGridRowGroupLayout::GetMinSize(aBox, aBoxLayoutState); |
michael@0 | 53 | |
michael@0 | 54 | nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox); |
michael@0 | 55 | if (frame) { |
michael@0 | 56 | nscoord rowheight = frame->GetRowHeightAppUnits(); |
michael@0 | 57 | minSize.height = frame->GetRowCount() * rowheight; |
michael@0 | 58 | // Pad the height. |
michael@0 | 59 | nscoord y = frame->GetAvailableHeight(); |
michael@0 | 60 | if (minSize.height > y && y > 0 && rowheight > 0) { |
michael@0 | 61 | nscoord m = (minSize.height-y)%rowheight; |
michael@0 | 62 | nscoord remainder = m == 0 ? 0 : rowheight - m; |
michael@0 | 63 | minSize.height += remainder; |
michael@0 | 64 | } |
michael@0 | 65 | if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None, |
michael@0 | 66 | nsGkAtoms::sizemode)) { |
michael@0 | 67 | nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState); |
michael@0 | 68 | if (width > minSize.width) |
michael@0 | 69 | minSize.width = width; |
michael@0 | 70 | } |
michael@0 | 71 | } |
michael@0 | 72 | return minSize; |
michael@0 | 73 | } |
michael@0 | 74 | |
michael@0 | 75 | nsSize |
michael@0 | 76 | nsListBoxLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState) |
michael@0 | 77 | { |
michael@0 | 78 | nsSize maxSize = nsGridRowGroupLayout::GetMaxSize(aBox, aBoxLayoutState); |
michael@0 | 79 | |
michael@0 | 80 | nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox); |
michael@0 | 81 | if (frame) { |
michael@0 | 82 | nscoord rowheight = frame->GetRowHeightAppUnits(); |
michael@0 | 83 | maxSize.height = frame->GetRowCount() * rowheight; |
michael@0 | 84 | // Pad the height. |
michael@0 | 85 | nscoord y = frame->GetAvailableHeight(); |
michael@0 | 86 | if (maxSize.height > y && y > 0 && rowheight > 0) { |
michael@0 | 87 | nscoord m = (maxSize.height-y)%rowheight; |
michael@0 | 88 | nscoord remainder = m == 0 ? 0 : rowheight - m; |
michael@0 | 89 | maxSize.height += remainder; |
michael@0 | 90 | } |
michael@0 | 91 | } |
michael@0 | 92 | return maxSize; |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | NS_IMETHODIMP |
michael@0 | 96 | nsListBoxLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState) |
michael@0 | 97 | { |
michael@0 | 98 | return LayoutInternal(aBox, aState); |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | |
michael@0 | 102 | /////////// nsListBoxLayout ///////////////////////// |
michael@0 | 103 | |
michael@0 | 104 | /** |
michael@0 | 105 | * Called to layout our our children. Does no frame construction |
michael@0 | 106 | */ |
michael@0 | 107 | NS_IMETHODIMP |
michael@0 | 108 | nsListBoxLayout::LayoutInternal(nsIFrame* aBox, nsBoxLayoutState& aState) |
michael@0 | 109 | { |
michael@0 | 110 | int32_t redrawStart = -1; |
michael@0 | 111 | |
michael@0 | 112 | // Get the start y position. |
michael@0 | 113 | nsListBoxBodyFrame* body = static_cast<nsListBoxBodyFrame*>(aBox); |
michael@0 | 114 | if (!body) { |
michael@0 | 115 | NS_ERROR("Frame encountered that isn't a listboxbody!"); |
michael@0 | 116 | return NS_ERROR_FAILURE; |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | nsMargin margin; |
michael@0 | 120 | |
michael@0 | 121 | // Get our client rect. |
michael@0 | 122 | nsRect clientRect; |
michael@0 | 123 | aBox->GetClientRect(clientRect); |
michael@0 | 124 | |
michael@0 | 125 | // Get the starting y position and the remaining available |
michael@0 | 126 | // height. |
michael@0 | 127 | nscoord availableHeight = body->GetAvailableHeight(); |
michael@0 | 128 | nscoord yOffset = body->GetYPosition(); |
michael@0 | 129 | |
michael@0 | 130 | if (availableHeight <= 0) { |
michael@0 | 131 | bool fixed = (body->GetFixedRowSize() != -1); |
michael@0 | 132 | if (fixed) |
michael@0 | 133 | availableHeight = 10; |
michael@0 | 134 | else |
michael@0 | 135 | return NS_OK; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | // run through all our currently created children |
michael@0 | 139 | nsIFrame* box = body->GetChildBox(); |
michael@0 | 140 | |
michael@0 | 141 | // if the reason is resize or initial we must relayout. |
michael@0 | 142 | nscoord rowHeight = body->GetRowHeightAppUnits(); |
michael@0 | 143 | |
michael@0 | 144 | while (box) { |
michael@0 | 145 | // If this box is dirty or if it has dirty children, we |
michael@0 | 146 | // call layout on it. |
michael@0 | 147 | nsRect childRect(box->GetRect()); |
michael@0 | 148 | box->GetMargin(margin); |
michael@0 | 149 | |
michael@0 | 150 | // relayout if we must or we are dirty or some of our children are dirty |
michael@0 | 151 | // or the client area is wider than us |
michael@0 | 152 | // XXXldb There should probably be a resize check here too! |
michael@0 | 153 | if (NS_SUBTREE_DIRTY(box) || childRect.width < clientRect.width) { |
michael@0 | 154 | childRect.x = 0; |
michael@0 | 155 | childRect.y = yOffset; |
michael@0 | 156 | childRect.width = clientRect.width; |
michael@0 | 157 | |
michael@0 | 158 | nsSize size = box->GetPrefSize(aState); |
michael@0 | 159 | body->SetRowHeight(size.height); |
michael@0 | 160 | |
michael@0 | 161 | childRect.height = rowHeight; |
michael@0 | 162 | |
michael@0 | 163 | childRect.Deflate(margin); |
michael@0 | 164 | box->SetBounds(aState, childRect); |
michael@0 | 165 | box->Layout(aState); |
michael@0 | 166 | } else { |
michael@0 | 167 | // if the child did not need to be relayed out. Then its easy. |
michael@0 | 168 | // Place the child by just grabbing its rect and adjusting the y. |
michael@0 | 169 | int32_t newPos = yOffset+margin.top; |
michael@0 | 170 | |
michael@0 | 171 | // are we pushing down or pulling up any rows? |
michael@0 | 172 | // Then we may have to redraw everything below the moved |
michael@0 | 173 | // rows. |
michael@0 | 174 | if (redrawStart == -1 && childRect.y != newPos) |
michael@0 | 175 | redrawStart = newPos; |
michael@0 | 176 | |
michael@0 | 177 | childRect.y = newPos; |
michael@0 | 178 | box->SetBounds(aState, childRect); |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | // Ok now the available size gets smaller and we move the |
michael@0 | 182 | // starting position of the next child down some. |
michael@0 | 183 | nscoord size = childRect.height + margin.top + margin.bottom; |
michael@0 | 184 | |
michael@0 | 185 | yOffset += size; |
michael@0 | 186 | availableHeight -= size; |
michael@0 | 187 | |
michael@0 | 188 | box = box->GetNextBox(); |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | // We have enough available height left to add some more rows |
michael@0 | 192 | // Since we can't do this during layout, we post a callback |
michael@0 | 193 | // that will be processed after the reflow completes. |
michael@0 | 194 | body->PostReflowCallback(); |
michael@0 | 195 | |
michael@0 | 196 | // if rows were pushed down or pulled up because some rows were added |
michael@0 | 197 | // before them then redraw everything under the inserted rows. The inserted |
michael@0 | 198 | // rows will automatically be redrawn because the were marked dirty on insertion. |
michael@0 | 199 | if (redrawStart > -1) { |
michael@0 | 200 | aBox->Redraw(aState); |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | return NS_OK; |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | // Creation Routines /////////////////////////////////////////////////////////////////////// |
michael@0 | 207 | |
michael@0 | 208 | already_AddRefed<nsBoxLayout> NS_NewListBoxLayout() |
michael@0 | 209 | { |
michael@0 | 210 | nsRefPtr<nsBoxLayout> layout = new nsListBoxLayout(); |
michael@0 | 211 | return layout.forget(); |
michael@0 | 212 | } |