2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
6 #include "SimpleLayouter.h"
10 #include <LayoutUtils.h>
15 // no lround() under BeOS R5 x86
16 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
17 # define lround(x) (long)floor((x) + 0.5)
22 class SimpleLayouter::ElementLayoutInfo
{
35 class SimpleLayouter::ElementInfo
{
47 max(B_SIZE_UNLIMITED
),
54 ElementInfo(int index
)
57 max(B_SIZE_UNLIMITED
),
64 void Assign(const ElementInfo
& info
)
68 preferred
= info
.preferred
;
70 tempWeight
= info
.tempWeight
;
75 class SimpleLayouter::MyLayoutInfo
: public LayoutInfo
{
78 ElementLayoutInfo
* fElements
;
81 MyLayoutInfo(int32 elementCount
)
83 fElementCount(elementCount
)
85 fElements
= new ElementLayoutInfo
[elementCount
];
88 virtual ~MyLayoutInfo()
93 virtual float ElementLocation(int32 element
)
95 if (element
< 0 || element
>= fElementCount
) {
100 return fElements
[element
].location
;
103 virtual float ElementSize(int32 element
)
105 if (element
< 0 || element
>= fElementCount
) {
110 return fElements
[element
].size
- 1;
116 SimpleLayouter::SimpleLayouter(int32 elementCount
, float spacing
)
117 : fElementCount(elementCount
),
118 fSpacing((int32
)spacing
),
120 fMax(B_SIZE_UNLIMITED
),
125 fElements
= new ElementInfo
[elementCount
];
126 for (int i
= 0; i
< elementCount
; i
++)
127 fElements
[i
].index
= i
;
131 SimpleLayouter::~SimpleLayouter()
138 SimpleLayouter::AddConstraints(int32 element
, int32 length
,
139 float _min
, float _max
, float _preferred
)
141 if (element
< 0 || element
>= fElementCount
) {
150 int32 min
= (int32
)_min
+ 1;
151 int32 max
= (int32
)_max
+ 1;
152 // int32 preferred = (int32)_preferred + 1;
154 ElementInfo
& info
= fElements
[element
];
155 info
.min
= max_c(info
.min
, min
);
156 info
.max
= min_c(info
.max
, max
);
157 info
.preferred
= max_c(info
.min
, min
);
159 fMinMaxValid
= false;
164 SimpleLayouter::SetWeight(int32 element
, float weight
)
166 if (element
< 0 || element
>= fElementCount
) {
171 fElements
[element
].weight
= weight
;
176 SimpleLayouter::MinSize()
184 SimpleLayouter::MaxSize()
192 SimpleLayouter::PreferredSize()
195 return fPreferred
- 1;
200 SimpleLayouter::CreateLayoutInfo()
202 return new MyLayoutInfo(fElementCount
);
207 SimpleLayouter::Layout(LayoutInfo
* layoutInfo
, float _size
)
209 int32 size
= int32(_size
+ 1);
211 fLayoutInfo
= (MyLayoutInfo
*)layoutInfo
;
215 if (fElementCount
== 0)
218 fLayoutInfo
->fSize
= max_c(size
, fMin
);
220 // layout the elements
221 if (fLayoutInfo
->fSize
>= fMax
)
228 for (int i
= 0; i
< fElementCount
; i
++) {
229 fLayoutInfo
->fElements
[i
].location
= location
;
230 location
+= fSpacing
+ fLayoutInfo
->fElements
[i
].size
;
236 SimpleLayouter::CloneLayouter()
238 SimpleLayouter
* layouter
= new SimpleLayouter(fElementCount
, fSpacing
);
240 for (int i
= 0; i
< fElementCount
; i
++)
241 layouter
->fElements
[i
].Assign(fElements
[i
]);
243 layouter
->fMin
= fMin
;
244 layouter
->fMax
= fMax
;
245 layouter
->fPreferred
= fPreferred
;
252 SimpleLayouter::DistributeSize(int32 size
, float weights
[], int32 sizes
[],
255 // create element infos
256 BList
elementInfos(count
);
257 for (int32 i
= 0; i
< count
; i
++) {
258 ElementInfo
* info
= new ElementInfo(i
);
259 info
->weight
= weights
[i
];
260 elementInfos
.AddItem(info
);
263 // compute integer weights
264 int64 sumWeight
= _CalculateSumWeight(elementInfos
);
266 // distribute the size
269 for (int32 i
= 0; i
< count
; i
++) {
270 ElementInfo
* info
= (ElementInfo
*)elementInfos
.ItemAt(i
);
271 weight
+= info
->tempWeight
;
272 int32 oldSumSize
= sumSize
;
273 sumSize
= (int32
)(size
* weight
/ sumWeight
);
274 sizes
[i
] = sumSize
- oldSumSize
;
280 // _CalculateSumWeight
282 SimpleLayouter::_CalculateSumWeight(BList
& elementInfos
)
284 if (elementInfos
.IsEmpty())
286 int32 count
= elementInfos
.CountItems();
288 // sum up the floating point weight, so we get a scale
290 for (int32 i
= 0; i
< count
; i
++) {
291 ElementInfo
* info
= (ElementInfo
*)elementInfos
.ItemAt(i
);
292 scale
+= info
->weight
;
298 // The weight sum is 0: We assign each info a temporary weight of 1.
299 for (int32 i
= 0; i
< count
; i
++) {
300 ElementInfo
* info
= (ElementInfo
*)elementInfos
.ItemAt(i
);
301 info
->tempWeight
= 1;
302 weight
+= info
->tempWeight
;
305 // We scale the weights so that their sum is about 100000. This should
306 // give us ample resolution. If possible make the scale integer, so that
307 // integer weights will produce exact results.
308 if (scale
>= 1 && scale
<= 100000)
309 scale
= lround(100000 / scale
);
311 scale
= 100000 / scale
;
313 for (int32 i
= 0; i
< count
; i
++) {
314 ElementInfo
* info
= (ElementInfo
*)elementInfos
.ItemAt(i
);
315 info
->tempWeight
= (int64
)(info
->weight
* scale
);
316 weight
+= info
->tempWeight
;
325 SimpleLayouter::_ValidateMinMax()
332 if (fElementCount
== 0) {
334 fMax
= B_SIZE_UNLIMITED
;
339 int spacing
= (fElementCount
- 1) * fSpacing
;
342 fPreferred
= spacing
;
344 for (int i
= 0; i
< fElementCount
; i
++) {
345 ElementInfo
& info
= fElements
[i
];
347 // correct the preferred and maximum sizes
348 if (info
.max
< info
.min
)
350 if (info
.preferred
< info
.min
)
351 info
.preferred
= info
.min
;
352 else if (info
.preferred
> info
.max
)
353 info
.preferred
= info
.max
;
357 fMax
= BLayoutUtils::AddSizesInt32(fMax
, info
.max
);
358 fPreferred
= BLayoutUtils::AddSizesInt32(fPreferred
, info
.preferred
);
364 SimpleLayouter::_LayoutMax()
366 ElementInfo
* infos
= fElements
;
367 int32 count
= fElementCount
;
371 int32 additionalSpace
= fLayoutInfo
->fSize
- fMax
;
373 // layout to the maximum first
374 for (int i
= 0; i
< count
; i
++)
375 fLayoutInfo
->fElements
[infos
[i
].index
].size
= infos
[i
].max
;
377 // Mmh, distributing according to the weights doesn't look that good.
378 // // Now distribute the additional space according to the weights.
379 // int64 sumWeight = calculateSumWeight(Arrays.asList(infos));
381 // int64 sumSize = 0;
382 // for (int i = 0; i < infos.length; i++) {
383 // weight += infos[i].tempWeight;
384 // int64 oldSumSize = sumSize;
385 // sumSize = (int)(additionalSpace * weight / sumWeight);
386 // fLayoutInfo.fElements[infos[i].index].size += sumSize - oldSumSize;
389 // distribute the additional space equally
391 for (int i
= 0; i
< count
; i
++) {
392 int64 oldSumSize
= sumSize
;
393 sumSize
= additionalSpace
* (i
+ 1) / count
;
394 fLayoutInfo
->fElements
[infos
[i
].index
].size
395 += int32(sumSize
- oldSumSize
);
401 SimpleLayouter::_LayoutStandard()
403 int32 space
= fLayoutInfo
->fSize
- (fElementCount
- 1) * fSpacing
;
405 BList
infosToLayout(fElementCount
);
406 for (int i
= 0; i
< fElementCount
; i
++) {
407 infosToLayout
.AddItem(&fElements
[i
]);
408 fLayoutInfo
->fElements
[i
].size
= 0;
411 BList
infosUnderMax(fElementCount
);
412 BList
infosOverMin(fElementCount
);
413 while (infosToLayout
.CountItems() > 0) {
414 int32 remainingSpace
= 0;
415 int32 infoCount
= infosToLayout
.CountItems();
416 int64 sumWeight
= _CalculateSumWeight(infosToLayout
);
417 int64 assignedWeight
= 0;
418 int32 assignedSize
= 0;
420 for (int32 i
= 0; i
< infoCount
; i
++) {
421 ElementInfo
* info
= (ElementInfo
*)infosToLayout
.ItemAt(i
);
422 ElementLayoutInfo
& layoutInfo
= fLayoutInfo
->fElements
[info
->index
];
423 // The simple algorithm is this:
424 // info.size += (int)(space * info.tempWeight / sumWeight);
425 // I.e. we simply assign space according to the weight. To avoid the
426 // rounding problematic, we make it a bit more complicated. We
427 // assign the difference of total assignment for all infos including
428 // the current one minus the total excluding the current one.
429 assignedWeight
+= info
->tempWeight
;
430 int32 oldAssignedSize
= assignedSize
;
431 assignedSize
= (int32
)(space
* assignedWeight
/ sumWeight
);
432 layoutInfo
.size
+= assignedSize
- oldAssignedSize
;
434 if (layoutInfo
.size
< info
->min
) {
435 remainingSpace
+= layoutInfo
.size
- info
->min
;
436 layoutInfo
.size
= info
->min
;
437 } else if (layoutInfo
.size
> info
->max
) {
438 remainingSpace
+= layoutInfo
.size
- info
->max
;
439 layoutInfo
.size
= info
->max
;
442 if (layoutInfo
.size
> info
->min
)
443 infosOverMin
.AddItem(info
);
444 if (layoutInfo
.size
< info
->max
)
445 infosUnderMax
.AddItem(info
);
448 infosToLayout
.MakeEmpty();
449 if (remainingSpace
> 0)
450 infosToLayout
.AddList(&infosUnderMax
);
451 else if (remainingSpace
< 0)
452 infosToLayout
.AddList(&infosOverMin
);
453 infosUnderMax
.MakeEmpty();
454 infosOverMin
.MakeEmpty();
455 space
= remainingSpace
;