xref: /haiku/src/kits/interface/layouter/CollapsingLayouter.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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