1 /* 2 * Copyright 2011, Haiku, Inc. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include "CollapsingLayouter.h" 7 8 #include "ComplexLayouter.h" 9 #include "OneElementLayouter.h" 10 #include "SimpleLayouter.h" 11 12 #include <ObjectList.h> 13 #include <Size.h> 14 15 16 class CollapsingLayouter::ProxyLayoutInfo : public LayoutInfo { 17 public: 18 ProxyLayoutInfo(LayoutInfo* target, int32 elementCount) 19 : 20 fTarget(target), 21 fElementCount(elementCount) 22 { 23 fElements = new int32[elementCount]; 24 } 25 26 ~ProxyLayoutInfo() 27 { 28 delete[] fElements; 29 delete fTarget; 30 } 31 32 void 33 LayoutTarget(Layouter* layouter, float size) 34 { 35 if (layouter) 36 layouter->Layout(fTarget, size); 37 } 38 39 void 40 SetElementPosition(int32 element, int32 position) 41 { 42 fElements[element] = position; 43 } 44 45 float 46 ElementLocation(int32 element) 47 { 48 if (element < 0 || element >= fElementCount || fElements[element] < 0) 49 return 0; 50 return fTarget->ElementLocation(fElements[element]); 51 } 52 53 float 54 ElementSize(int32 element) 55 { 56 if (element < 0 || element >= fElementCount || fElements[element] < 0) 57 return 0; 58 return fTarget->ElementSize(fElements[element]); 59 } 60 61 float 62 ElementRangeSize(int32 element, int32 length) 63 { 64 if (element < 0 || element >= fElementCount || fElements[element] < 0) 65 return 0; 66 return fTarget->ElementRangeSize(fElements[element], length); 67 } 68 69 private: 70 int32* fElements; 71 LayoutInfo* fTarget; 72 int32 fElementCount; 73 }; 74 75 76 struct CollapsingLayouter::Constraint { 77 int32 length; 78 float min; 79 float max; 80 float preferred; 81 }; 82 83 84 struct CollapsingLayouter::ElementInfo { 85 float weight; 86 int32 position; 87 bool valid; 88 BObjectList<Constraint> constraints; 89 90 ElementInfo() 91 : 92 weight(0), 93 position(-1), 94 valid(false), 95 constraints(5, true) 96 { 97 } 98 99 ~ElementInfo() 100 { 101 } 102 103 void SetTo(const ElementInfo& other) 104 { 105 weight = other.weight; 106 position = other.position; 107 valid = other.valid; 108 for (int32 i = other.constraints.CountItems() - 1; i >= 0; i--) 109 constraints.AddItem(new Constraint(*other.constraints.ItemAt(i))); 110 } 111 }; 112 113 114 CollapsingLayouter::CollapsingLayouter(int32 elementCount, float spacing) 115 : 116 fElementCount(elementCount), 117 fElements(new ElementInfo[elementCount]), 118 fValidElementCount(0), 119 fHaveMultiElementConstraints(false), 120 fSpacing(spacing), 121 fLayouter(NULL) 122 { 123 } 124 125 126 CollapsingLayouter::~CollapsingLayouter() 127 { 128 delete[] fElements; 129 delete fLayouter; 130 } 131 132 133 void 134 CollapsingLayouter::AddConstraints(int32 element, int32 length, float min, 135 float max, float preferred) 136 { 137 if (min == B_SIZE_UNSET && max == B_SIZE_UNSET) 138 return; 139 if (element < 0 || length <= 0 || element + length > fElementCount) 140 return; 141 142 Constraint* constraint = new Constraint(); 143 constraint->length = length; 144 constraint->min = min; 145 constraint->max = max; 146 constraint->preferred = preferred; 147 148 if (length > 1) 149 fHaveMultiElementConstraints = true; 150 151 int32 validElements = fValidElementCount; 152 153 for (int32 i = element; i < element + length; i++) { 154 if (fElements[i].valid == false) { 155 fElements[i].valid = true; 156 fValidElementCount++; 157 } 158 } 159 160 fElements[element].constraints.AddItem(constraint); 161 if (fValidElementCount > validElements) { 162 delete fLayouter; 163 fLayouter = NULL; 164 } 165 166 if (fLayouter) 167 _AddConstraints(element, constraint); 168 169 } 170 171 172 void 173 CollapsingLayouter::SetWeight(int32 element, float weight) 174 { 175 if (element < 0 || element >= fElementCount) 176 return; 177 178 ElementInfo& elementInfo = fElements[element]; 179 elementInfo.weight = weight; 180 181 if (fLayouter && elementInfo.position >= 0) 182 fLayouter->SetWeight(elementInfo.position, weight); 183 } 184 185 186 float 187 CollapsingLayouter::MinSize() 188 { 189 _ValidateLayouter(); 190 return fLayouter ? fLayouter->MinSize() : 0; 191 } 192 193 194 float 195 CollapsingLayouter::MaxSize() 196 { 197 _ValidateLayouter(); 198 return fLayouter ? fLayouter->MaxSize() : B_SIZE_UNLIMITED; 199 } 200 201 202 float 203 CollapsingLayouter::PreferredSize() 204 { 205 _ValidateLayouter(); 206 return fLayouter ? fLayouter->PreferredSize() : 0; 207 } 208 209 210 LayoutInfo* 211 CollapsingLayouter::CreateLayoutInfo() 212 { 213 _ValidateLayouter(); 214 215 LayoutInfo* info = fLayouter ? fLayouter->CreateLayoutInfo() : NULL; 216 return new ProxyLayoutInfo(info, fElementCount); 217 } 218 219 220 void 221 CollapsingLayouter::Layout(LayoutInfo* layoutInfo, float size) 222 { 223 _ValidateLayouter(); 224 ProxyLayoutInfo* info = static_cast<ProxyLayoutInfo*>(layoutInfo); 225 for (int32 i = 0; i < fElementCount; i++) { 226 info->SetElementPosition(i, fElements[i].position); 227 } 228 229 info->LayoutTarget(fLayouter, size); 230 } 231 232 233 Layouter* 234 CollapsingLayouter::CloneLayouter() 235 { 236 CollapsingLayouter* clone = new CollapsingLayouter(fElementCount, fSpacing); 237 for (int32 i = 0; i < fElementCount; i++) 238 clone->fElements[i].SetTo(fElements[i]); 239 240 clone->fValidElementCount = fValidElementCount; 241 clone->fHaveMultiElementConstraints = fHaveMultiElementConstraints; 242 243 if (fLayouter) 244 clone->fLayouter = fLayouter->CloneLayouter(); 245 return clone; 246 } 247 248 249 void 250 CollapsingLayouter::_ValidateLayouter() 251 { 252 if (fLayouter) 253 return; 254 255 _CreateLayouter(); 256 _DoCollapse(); 257 _AddConstraints(); 258 _SetWeights(); 259 } 260 261 262 Layouter* 263 CollapsingLayouter::_CreateLayouter() 264 { 265 if (fLayouter) 266 return fLayouter; 267 268 if (fValidElementCount == 0) { 269 fLayouter = NULL; 270 } else if (fValidElementCount == 1) { 271 fLayouter = new OneElementLayouter(); 272 } else if (fHaveMultiElementConstraints) { 273 fLayouter = new ComplexLayouter(fValidElementCount, fSpacing); 274 } else { 275 fLayouter = new SimpleLayouter(fValidElementCount, fSpacing); 276 } 277 278 return fLayouter; 279 } 280 281 282 void 283 CollapsingLayouter::_DoCollapse() 284 { 285 int32 shift = 0; 286 for (int32 i = 0; i < fElementCount; i++) { 287 ElementInfo& element = fElements[i]; 288 if (!element.valid) { 289 shift++; 290 element.position = -1; 291 continue; 292 } else { 293 element.position = i - shift; 294 } 295 } 296 } 297 298 299 void 300 CollapsingLayouter::_AddConstraints() 301 { 302 if (fLayouter == NULL) 303 return; 304 305 for (int32 i = 0; i < fElementCount; i++) { 306 ElementInfo& element = fElements[i]; 307 for (int32 i = element.constraints.CountItems() - 1; i >= 0; i--) 308 _AddConstraints(element.position, element.constraints.ItemAt(i)); 309 } 310 } 311 312 313 void 314 CollapsingLayouter::_AddConstraints(int32 position, const Constraint* c) 315 { 316 fLayouter->AddConstraints(position, c->length, c->min, c->max, 317 c->preferred); 318 } 319 320 321 void 322 CollapsingLayouter::_SetWeights() 323 { 324 if (!fLayouter) 325 return; 326 327 for (int32 i = 0; i < fElementCount; i++) { 328 fLayouter->SetWeight(fElements[i].position, fElements[i].weight); 329 } 330 } 331