178 minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).height(); |
205 minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).height(); |
179 prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).height(); |
206 prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).height(); |
180 maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height(); |
207 maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height(); |
181 } |
208 } |
182 |
209 |
183 if (hasCenter) { |
210 if (isCenterAnchor) { |
184 minSizeHint /= 2; |
211 minSizeHint /= 2; |
185 prefSizeHint /= 2; |
212 prefSizeHint /= 2; |
186 maxSizeHint /= 2; |
213 maxSizeHint /= 2; |
187 } |
214 } |
188 } |
215 } |
189 } else { |
216 } else { |
|
217 // It is a user-created anchor, fetch size information from the associated QGraphicsAnchor |
190 Q_ASSERT(graphicsAnchor); |
218 Q_ASSERT(graphicsAnchor); |
191 policy = graphicsAnchor->sizePolicy(); |
219 QGraphicsAnchorPrivate *anchorPrivate = graphicsAnchor->d_func(); |
|
220 |
|
221 // Policy, min and max sizes are straightforward |
|
222 policy = anchorPrivate->sizePolicy; |
192 minSizeHint = 0; |
223 minSizeHint = 0; |
193 if (hasSize) { |
224 maxSizeHint = QWIDGETSIZE_MAX; |
194 // One can only configure the preferred size of a normal anchor. Their minimum and |
225 |
195 // maximum "size hints" are always 0 and QWIDGETSIZE_MAX, correspondingly. However, |
226 // Preferred Size |
196 // their effective size hints might be narrowed down due to their size policies. |
227 if (anchorPrivate->hasSize) { |
197 prefSizeHint = prefSize; |
228 // Anchor has user-defined size |
|
229 prefSizeHint = anchorPrivate->preferredSize; |
198 } else { |
230 } else { |
199 prefSizeHint = effectiveSpacing; |
231 // Fetch size information from style |
200 } |
232 const Qt::Orientation orient = Qt::Orientation(QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge) + 1); |
201 maxSizeHint = QWIDGETSIZE_MAX; |
233 qreal s = styleInfo->defaultSpacing(orient); |
202 } |
234 if (s < 0) { |
203 internalSizeHints(policy, minSizeHint, prefSizeHint, maxSizeHint, |
235 QSizePolicy::ControlType controlTypeFrom = from->m_item->sizePolicy().controlType(); |
204 &minSize, &prefSize, &expSize, &maxSize); |
236 QSizePolicy::ControlType controlTypeTo = to->m_item->sizePolicy().controlType(); |
|
237 s = styleInfo->perItemSpacing(controlTypeFrom, controlTypeTo, orient); |
|
238 |
|
239 // ### Currently we do not support negative anchors inside the graph. |
|
240 // To avoid those being created by a negative style spacing, we must |
|
241 // make this test. |
|
242 if (s < 0) |
|
243 s = 0; |
|
244 } |
|
245 prefSizeHint = s; |
|
246 } |
|
247 } |
|
248 |
|
249 // Fill minSize, prefSize and maxSize based on policy and sizeHints |
|
250 applySizePolicy(policy, minSizeHint, prefSizeHint, maxSizeHint, |
|
251 &minSize, &prefSize, &maxSize); |
|
252 |
|
253 minPrefSize = prefSize; |
|
254 maxPrefSize = maxSize; |
205 |
255 |
206 // Set the anchor effective sizes to preferred. |
256 // Set the anchor effective sizes to preferred. |
207 // |
257 // |
208 // Note: The idea here is that all items should remain at their |
258 // Note: The idea here is that all items should remain at their |
209 // preferred size unless where that's impossible. In cases where |
259 // preferred size unless where that's impossible. In cases where |
210 // the item is subject to restrictions (anchored to the layout |
260 // the item is subject to restrictions (anchored to the layout |
211 // edges, for instance), the simplex solver will be run to |
261 // edges, for instance), the simplex solver will be run to |
212 // recalculate and override the values we set here. |
262 // recalculate and override the values we set here. |
213 sizeAtMinimum = prefSize; |
263 sizeAtMinimum = prefSize; |
214 sizeAtPreferred = prefSize; |
264 sizeAtPreferred = prefSize; |
215 sizeAtExpanding = prefSize; |
|
216 sizeAtMaximum = prefSize; |
265 sizeAtMaximum = prefSize; |
217 } |
266 } |
218 |
267 |
219 void ParallelAnchorData::updateChildrenSizes() |
268 void ParallelAnchorData::updateChildrenSizes() |
220 { |
269 { |
221 firstEdge->sizeAtMinimum = secondEdge->sizeAtMinimum = sizeAtMinimum; |
270 firstEdge->sizeAtMinimum = sizeAtMinimum; |
222 firstEdge->sizeAtPreferred = secondEdge->sizeAtPreferred = sizeAtPreferred; |
271 firstEdge->sizeAtPreferred = sizeAtPreferred; |
223 firstEdge->sizeAtExpanding = secondEdge->sizeAtExpanding = sizeAtExpanding; |
272 firstEdge->sizeAtMaximum = sizeAtMaximum; |
224 firstEdge->sizeAtMaximum = secondEdge->sizeAtMaximum = sizeAtMaximum; |
273 |
|
274 if (secondForward()) { |
|
275 secondEdge->sizeAtMinimum = sizeAtMinimum; |
|
276 secondEdge->sizeAtPreferred = sizeAtPreferred; |
|
277 secondEdge->sizeAtMaximum = sizeAtMaximum; |
|
278 } else { |
|
279 secondEdge->sizeAtMinimum = -sizeAtMinimum; |
|
280 secondEdge->sizeAtPreferred = -sizeAtPreferred; |
|
281 secondEdge->sizeAtMaximum = -sizeAtMaximum; |
|
282 } |
225 |
283 |
226 firstEdge->updateChildrenSizes(); |
284 firstEdge->updateChildrenSizes(); |
227 secondEdge->updateChildrenSizes(); |
285 secondEdge->updateChildrenSizes(); |
228 } |
286 } |
229 |
287 |
230 void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing) |
288 /* |
231 { |
289 \internal |
232 refreshSizeHints_helper(effectiveSpacing); |
290 |
233 } |
291 Initialize the parallel anchor size hints using the sizeHint information from |
234 |
292 its children. |
235 void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, |
293 |
236 bool refreshChildren) |
294 Note that parallel groups can lead to unfeasibility, so during calculation, we can |
237 { |
295 find out one unfeasibility. Because of that this method return boolean. This can't |
238 if (refreshChildren) { |
296 happen in sequential, so there the method is void. |
239 firstEdge->refreshSizeHints(effectiveSpacing); |
297 */ |
240 secondEdge->refreshSizeHints(effectiveSpacing); |
298 bool ParallelAnchorData::calculateSizeHints() |
241 } |
299 { |
242 |
300 // Normalize second child sizes. |
243 // ### should we warn if the parallel connection is invalid? |
301 // A negative anchor of sizes min, minPref, pref, maxPref and max, is equivalent |
244 // e.g. 1-2-3 with 10-20-30, the minimum of the latter is |
302 // to a forward anchor of sizes -max, -maxPref, -pref, -minPref, -min |
245 // bigger than the maximum of the former. |
303 qreal secondMin; |
246 |
304 qreal secondMinPref; |
247 minSize = qMax(firstEdge->minSize, secondEdge->minSize); |
305 qreal secondPref; |
248 maxSize = qMin(firstEdge->maxSize, secondEdge->maxSize); |
306 qreal secondMaxPref; |
249 |
307 qreal secondMax; |
250 expSize = qMax(firstEdge->expSize, secondEdge->expSize); |
308 |
251 expSize = qMin(expSize, maxSize); |
309 if (secondForward()) { |
252 |
310 secondMin = secondEdge->minSize; |
253 prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); |
311 secondMinPref = secondEdge->minPrefSize; |
254 prefSize = qMin(prefSize, expSize); |
312 secondPref = secondEdge->prefSize; |
|
313 secondMaxPref = secondEdge->maxPrefSize; |
|
314 secondMax = secondEdge->maxSize; |
|
315 } else { |
|
316 secondMin = -secondEdge->maxSize; |
|
317 secondMinPref = -secondEdge->maxPrefSize; |
|
318 secondPref = -secondEdge->prefSize; |
|
319 secondMaxPref = -secondEdge->minPrefSize; |
|
320 secondMax = -secondEdge->minSize; |
|
321 } |
|
322 |
|
323 minSize = qMax(firstEdge->minSize, secondMin); |
|
324 maxSize = qMin(firstEdge->maxSize, secondMax); |
|
325 |
|
326 // This condition means that the maximum size of one anchor being simplified is smaller than |
|
327 // the minimum size of the other anchor. The consequence is that there won't be a valid size |
|
328 // for this parallel setup. |
|
329 if (minSize > maxSize) { |
|
330 return false; |
|
331 } |
|
332 |
|
333 // Preferred size calculation |
|
334 // The calculation of preferred size is done as follows: |
|
335 // |
|
336 // 1) Check whether one of the child anchors is the layout structural anchor |
|
337 // If so, we can simply copy the preferred information from the other child, |
|
338 // after bounding it to our minimum and maximum sizes. |
|
339 // If not, then we proceed with the actual calculations. |
|
340 // |
|
341 // 2) The whole algorithm for preferred size calculation is based on the fact |
|
342 // that, if a given anchor cannot remain at its preferred size, it'd rather |
|
343 // grow than shrink. |
|
344 // |
|
345 // What happens though is that while this affirmative is true for simple |
|
346 // anchors, it may not be true for sequential anchors that have one or more |
|
347 // reversed anchors inside it. That happens because when a sequential anchor |
|
348 // grows, any reversed anchors inside it may be required to shrink, something |
|
349 // we try to avoid, as said above. |
|
350 // |
|
351 // To overcome this, besides their actual preferred size "prefSize", each anchor |
|
352 // exports what we call "minPrefSize" and "maxPrefSize". These two values define |
|
353 // a surrounding interval where, if required to move, the anchor would rather |
|
354 // remain inside. |
|
355 // |
|
356 // For standard anchors, this area simply represents the region between |
|
357 // prefSize and maxSize, which makes sense since our first affirmation. |
|
358 // For composed anchors, these values are calculated as to reduce the global |
|
359 // "damage", that is, to reduce the total deviation and the total amount of |
|
360 // anchors that had to shrink. |
|
361 |
|
362 if (firstEdge->isLayoutAnchor) { |
|
363 prefSize = qBound(minSize, secondPref, maxSize); |
|
364 minPrefSize = qBound(minSize, secondMinPref, maxSize); |
|
365 maxPrefSize = qBound(minSize, secondMaxPref, maxSize); |
|
366 } else if (secondEdge->isLayoutAnchor) { |
|
367 prefSize = qBound(minSize, firstEdge->prefSize, maxSize); |
|
368 minPrefSize = qBound(minSize, firstEdge->minPrefSize, maxSize); |
|
369 maxPrefSize = qBound(minSize, firstEdge->maxPrefSize, maxSize); |
|
370 } else { |
|
371 // Calculate the intersection between the "preferred" regions of each child |
|
372 const qreal lowerBoundary = |
|
373 qBound(minSize, qMax(firstEdge->minPrefSize, secondMinPref), maxSize); |
|
374 const qreal upperBoundary = |
|
375 qBound(minSize, qMin(firstEdge->maxPrefSize, secondMaxPref), maxSize); |
|
376 const qreal prefMean = |
|
377 qBound(minSize, (firstEdge->prefSize + secondPref) / 2, maxSize); |
|
378 |
|
379 if (lowerBoundary < upperBoundary) { |
|
380 // If there is an intersection between the two regions, this intersection |
|
381 // will be used as the preferred region of the parallel anchor itself. |
|
382 // The preferred size will be the bounded average between the two preferred |
|
383 // sizes. |
|
384 prefSize = qBound(lowerBoundary, prefMean, upperBoundary); |
|
385 minPrefSize = lowerBoundary; |
|
386 maxPrefSize = upperBoundary; |
|
387 } else { |
|
388 // If there is no intersection, we have to attribute "damage" to at least |
|
389 // one of the children. The minimum total damage is achieved in points |
|
390 // inside the region that extends from (1) the upper boundary of the lower |
|
391 // region to (2) the lower boundary of the upper region. |
|
392 // Then, we expose this region as _our_ preferred region and once again, |
|
393 // use the bounded average as our preferred size. |
|
394 prefSize = qBound(upperBoundary, prefMean, lowerBoundary); |
|
395 minPrefSize = upperBoundary; |
|
396 maxPrefSize = lowerBoundary; |
|
397 } |
|
398 } |
255 |
399 |
256 // See comment in AnchorData::refreshSizeHints() about sizeAt* values |
400 // See comment in AnchorData::refreshSizeHints() about sizeAt* values |
257 sizeAtMinimum = prefSize; |
401 sizeAtMinimum = prefSize; |
258 sizeAtPreferred = prefSize; |
402 sizeAtPreferred = prefSize; |
259 sizeAtExpanding = prefSize; |
|
260 sizeAtMaximum = prefSize; |
403 sizeAtMaximum = prefSize; |
|
404 |
|
405 return true; |
261 } |
406 } |
262 |
407 |
263 /*! |
408 /*! |
264 \internal |
409 \internal |
265 returns the factor in the interval [-1, 1]. |
410 returns the factor in the interval [-1, 1]. |
266 -1 is at Minimum |
411 -1 is at Minimum |
267 0 is at Preferred |
412 0 is at Preferred |
268 1 is at Maximum |
413 1 is at Maximum |
269 */ |
414 */ |
270 static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal value, qreal min, |
415 static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal value, qreal min, |
271 qreal pref, qreal exp, |
416 qreal minPref, qreal pref, |
272 qreal max) |
417 qreal maxPref, qreal max) |
273 { |
418 { |
274 QGraphicsAnchorLayoutPrivate::Interval interval; |
419 QGraphicsAnchorLayoutPrivate::Interval interval; |
275 qreal lower; |
420 qreal lower; |
276 qreal upper; |
421 qreal upper; |
277 |
422 |
278 if (value < pref) { |
423 if (value < minPref) { |
279 interval = QGraphicsAnchorLayoutPrivate::MinToPreferred; |
424 interval = QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred; |
280 lower = min; |
425 lower = min; |
|
426 upper = minPref; |
|
427 } else if (value < pref) { |
|
428 interval = QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred; |
|
429 lower = minPref; |
281 upper = pref; |
430 upper = pref; |
282 } else if (value < exp) { |
431 } else if (value < maxPref) { |
283 interval = QGraphicsAnchorLayoutPrivate::PreferredToExpanding; |
432 interval = QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred; |
284 lower = pref; |
433 lower = pref; |
285 upper = exp; |
434 upper = maxPref; |
286 } else { |
435 } else { |
287 interval = QGraphicsAnchorLayoutPrivate::ExpandingToMax; |
436 interval = QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum; |
288 lower = exp; |
437 lower = maxPref; |
289 upper = max; |
438 upper = max; |
290 } |
439 } |
291 |
440 |
292 qreal progress; |
441 qreal progress; |
293 if (upper == lower) { |
442 if (upper == lower) { |
298 |
447 |
299 return qMakePair(interval, progress); |
448 return qMakePair(interval, progress); |
300 } |
449 } |
301 |
450 |
302 static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor, |
451 static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor, |
303 qreal min, qreal pref, |
452 qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max) |
304 qreal exp, qreal max) |
|
305 { |
453 { |
306 qreal lower; |
454 qreal lower; |
307 qreal upper; |
455 qreal upper; |
308 |
456 |
309 switch (factor.first) { |
457 switch (factor.first) { |
310 case QGraphicsAnchorLayoutPrivate::MinToPreferred: |
458 case QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred: |
311 lower = min; |
459 lower = min; |
|
460 upper = minPref; |
|
461 break; |
|
462 case QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred: |
|
463 lower = minPref; |
312 upper = pref; |
464 upper = pref; |
313 break; |
465 break; |
314 case QGraphicsAnchorLayoutPrivate::PreferredToExpanding: |
466 case QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred: |
315 lower = pref; |
467 lower = pref; |
316 upper = exp; |
468 upper = maxPref; |
317 break; |
469 break; |
318 case QGraphicsAnchorLayoutPrivate::ExpandingToMax: |
470 case QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum: |
319 lower = exp; |
471 lower = maxPref; |
320 upper = max; |
472 upper = max; |
321 break; |
473 break; |
322 } |
474 } |
323 |
475 |
324 return lower + factor.second * (upper - lower); |
476 return lower + factor.second * (upper - lower); |
325 } |
477 } |
326 |
478 |
327 void SequentialAnchorData::updateChildrenSizes() |
479 void SequentialAnchorData::updateChildrenSizes() |
328 { |
480 { |
329 // ### REMOVE ME |
|
330 // ### check whether we are guarantee to get those or we need to warn stuff at this |
|
331 // point. |
|
332 Q_ASSERT(sizeAtMinimum > minSize || qFuzzyCompare(sizeAtMinimum, minSize)); |
|
333 Q_ASSERT(sizeAtMinimum < maxSize || qFuzzyCompare(sizeAtMinimum, maxSize)); |
|
334 Q_ASSERT(sizeAtPreferred > minSize || qFuzzyCompare(sizeAtPreferred, minSize)); |
|
335 Q_ASSERT(sizeAtPreferred < maxSize || qFuzzyCompare(sizeAtPreferred, maxSize)); |
|
336 Q_ASSERT(sizeAtExpanding > minSize || qFuzzyCompare(sizeAtExpanding, minSize)); |
|
337 Q_ASSERT(sizeAtExpanding < maxSize || qFuzzyCompare(sizeAtExpanding, maxSize)); |
|
338 Q_ASSERT(sizeAtMaximum > minSize || qFuzzyCompare(sizeAtMaximum, minSize)); |
|
339 Q_ASSERT(sizeAtMaximum < maxSize || qFuzzyCompare(sizeAtMaximum, maxSize)); |
|
340 |
|
341 // Band here refers if the value is in the Minimum To Preferred |
481 // Band here refers if the value is in the Minimum To Preferred |
342 // band (the lower band) or the Preferred To Maximum (the upper band). |
482 // band (the lower band) or the Preferred To Maximum (the upper band). |
343 |
483 |
344 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor = |
484 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor = |
345 getFactor(sizeAtMinimum, minSize, prefSize, expSize, maxSize); |
485 getFactor(sizeAtMinimum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize); |
346 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> prefFactor = |
486 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> prefFactor = |
347 getFactor(sizeAtPreferred, minSize, prefSize, expSize, maxSize); |
487 getFactor(sizeAtPreferred, minSize, minPrefSize, prefSize, maxPrefSize, maxSize); |
348 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> expFactor = |
|
349 getFactor(sizeAtExpanding, minSize, prefSize, expSize, maxSize); |
|
350 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> maxFactor = |
488 const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> maxFactor = |
351 getFactor(sizeAtMaximum, minSize, prefSize, expSize, maxSize); |
489 getFactor(sizeAtMaximum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize); |
|
490 |
|
491 // XXX This is not safe if Vertex simplification takes place after the sequential |
|
492 // anchor is created. In that case, "prev" will be a group-vertex, different from |
|
493 // "from" or "to", that _contains_ one of them. |
|
494 AnchorVertex *prev = from; |
352 |
495 |
353 for (int i = 0; i < m_edges.count(); ++i) { |
496 for (int i = 0; i < m_edges.count(); ++i) { |
354 AnchorData *e = m_edges.at(i); |
497 AnchorData *e = m_edges.at(i); |
355 |
498 |
356 e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); |
499 const bool edgeIsForward = (e->from == prev); |
357 e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); |
500 if (edgeIsForward) { |
358 e->sizeAtExpanding = interpolate(expFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); |
501 e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->minPrefSize, |
359 e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); |
502 e->prefSize, e->maxPrefSize, e->maxSize); |
|
503 e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->minPrefSize, |
|
504 e->prefSize, e->maxPrefSize, e->maxSize); |
|
505 e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->minPrefSize, |
|
506 e->prefSize, e->maxPrefSize, e->maxSize); |
|
507 prev = e->to; |
|
508 } else { |
|
509 Q_ASSERT(prev == e->to); |
|
510 e->sizeAtMinimum = interpolate(minFactor, e->maxSize, e->maxPrefSize, |
|
511 e->prefSize, e->minPrefSize, e->minSize); |
|
512 e->sizeAtPreferred = interpolate(prefFactor, e->maxSize, e->maxPrefSize, |
|
513 e->prefSize, e->minPrefSize, e->minSize); |
|
514 e->sizeAtMaximum = interpolate(maxFactor, e->maxSize, e->maxPrefSize, |
|
515 e->prefSize, e->minPrefSize, e->minSize); |
|
516 prev = e->from; |
|
517 } |
360 |
518 |
361 e->updateChildrenSizes(); |
519 e->updateChildrenSizes(); |
362 } |
520 } |
363 } |
521 } |
364 |
522 |
365 void SequentialAnchorData::refreshSizeHints(qreal effectiveSpacing) |
523 void SequentialAnchorData::calculateSizeHints() |
366 { |
|
367 refreshSizeHints_helper(effectiveSpacing); |
|
368 } |
|
369 |
|
370 void SequentialAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, |
|
371 bool refreshChildren) |
|
372 { |
524 { |
373 minSize = 0; |
525 minSize = 0; |
374 prefSize = 0; |
526 prefSize = 0; |
375 expSize = 0; |
|
376 maxSize = 0; |
527 maxSize = 0; |
|
528 minPrefSize = 0; |
|
529 maxPrefSize = 0; |
|
530 |
|
531 AnchorVertex *prev = from; |
377 |
532 |
378 for (int i = 0; i < m_edges.count(); ++i) { |
533 for (int i = 0; i < m_edges.count(); ++i) { |
379 AnchorData *edge = m_edges.at(i); |
534 AnchorData *edge = m_edges.at(i); |
380 |
535 |
381 // If it's the case refresh children information first |
536 const bool edgeIsForward = (edge->from == prev); |
382 if (refreshChildren) |
537 if (edgeIsForward) { |
383 edge->refreshSizeHints(effectiveSpacing); |
538 minSize += edge->minSize; |
384 |
539 prefSize += edge->prefSize; |
385 minSize += edge->minSize; |
540 maxSize += edge->maxSize; |
386 prefSize += edge->prefSize; |
541 minPrefSize += edge->minPrefSize; |
387 expSize += edge->expSize; |
542 maxPrefSize += edge->maxPrefSize; |
388 maxSize += edge->maxSize; |
543 prev = edge->to; |
|
544 } else { |
|
545 Q_ASSERT(prev == edge->to); |
|
546 minSize -= edge->maxSize; |
|
547 prefSize -= edge->prefSize; |
|
548 maxSize -= edge->minSize; |
|
549 minPrefSize -= edge->maxPrefSize; |
|
550 maxPrefSize -= edge->minPrefSize; |
|
551 prev = edge->from; |
|
552 } |
389 } |
553 } |
390 |
554 |
391 // See comment in AnchorData::refreshSizeHints() about sizeAt* values |
555 // See comment in AnchorData::refreshSizeHints() about sizeAt* values |
392 sizeAtMinimum = prefSize; |
556 sizeAtMinimum = prefSize; |
393 sizeAtPreferred = prefSize; |
557 sizeAtPreferred = prefSize; |
394 sizeAtExpanding = prefSize; |
|
395 sizeAtMaximum = prefSize; |
558 sizeAtMaximum = prefSize; |
396 } |
559 } |
397 |
560 |
398 #ifdef QT_DEBUG |
561 #ifdef QT_DEBUG |
399 void AnchorData::dump(int indent) { |
562 void AnchorData::dump(int indent) { |
508 return FLT_MAX; |
673 return FLT_MAX; |
509 return a + b; |
674 return a + b; |
510 } |
675 } |
511 |
676 |
512 /*! |
677 /*! |
513 * \internal |
678 \internal |
514 * |
679 |
515 * Takes the sequence of vertices described by (\a before, \a vertices, \a after) and replaces |
680 Adds \a newAnchor to the graph. |
516 * all anchors connected to the vertices in \a vertices with one simplified anchor between |
681 |
517 * \a before and \a after. The simplified anchor will be a placeholder for all the previous |
682 Returns the newAnchor itself if it could be added without further changes to the graph. If a |
518 * anchors between \a before and \a after, and can be restored back to the anchors it is a |
683 new parallel anchor had to be created, then returns the new parallel anchor. If a parallel anchor |
519 * placeholder for. |
684 had to be created and it results in an unfeasible setup, \a feasible is set to false, otherwise |
520 */ |
685 true. |
521 static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph, |
686 |
522 AnchorVertex *before, |
687 Note that in the case a new parallel anchor is created, it might also take over some constraints |
523 const QVector<AnchorVertex*> &vertices, |
688 from its children anchors. |
524 AnchorVertex *after) |
689 */ |
525 { |
690 AnchorData *QGraphicsAnchorLayoutPrivate::addAnchorMaybeParallel(AnchorData *newAnchor, bool *feasible) |
526 AnchorData *data = graph->edgeData(before, vertices.first()); |
691 { |
527 Q_ASSERT(data); |
692 Orientation orientation = Orientation(newAnchor->orientation); |
528 |
693 Graph<AnchorVertex, AnchorData> &g = graph[orientation]; |
529 const bool forward = (before == data->from); |
694 *feasible = true; |
530 QVector<AnchorVertex *> orderedVertices; |
695 |
531 |
696 // If already exists one anchor where newAnchor is supposed to be, we create a parallel |
532 if (forward) { |
697 // anchor. |
533 orderedVertices = vertices; |
698 if (AnchorData *oldAnchor = g.takeEdge(newAnchor->from, newAnchor->to)) { |
534 } else { |
699 ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, newAnchor); |
535 qSwap(before, after); |
700 |
536 for (int i = vertices.count() - 1; i >= 0; --i) |
701 // The parallel anchor will "replace" its children anchors in |
537 orderedVertices.append(vertices.at(i)); |
702 // every center constraint that they appear. |
538 } |
703 |
539 |
704 // ### If the dependent (center) anchors had reference(s) to their constraints, we |
|
705 // could avoid traversing all the itemCenterConstraints. |
|
706 QList<QSimplexConstraint *> &constraints = itemCenterConstraints[orientation]; |
|
707 |
|
708 AnchorData *children[2] = { oldAnchor, newAnchor }; |
|
709 QList<QSimplexConstraint *> *childrenConstraints[2] = { ¶llel->m_firstConstraints, |
|
710 ¶llel->m_secondConstraints }; |
|
711 |
|
712 for (int i = 0; i < 2; ++i) { |
|
713 AnchorData *child = children[i]; |
|
714 QList<QSimplexConstraint *> *childConstraints = childrenConstraints[i]; |
|
715 |
|
716 // We need to fix the second child constraints if the parallel group will have the |
|
717 // opposite direction of the second child anchor. For the point of view of external |
|
718 // entities, this anchor was reversed. So if at some point we say that the parallel |
|
719 // has a value of 20, this mean that the second child (when reversed) will be |
|
720 // assigned -20. |
|
721 const bool needsReverse = i == 1 && !parallel->secondForward(); |
|
722 |
|
723 if (!child->isCenterAnchor) |
|
724 continue; |
|
725 |
|
726 parallel->isCenterAnchor = true; |
|
727 |
|
728 for (int j = 0; j < constraints.count(); ++j) { |
|
729 QSimplexConstraint *c = constraints[j]; |
|
730 if (c->variables.contains(child)) { |
|
731 childConstraints->append(c); |
|
732 qreal v = c->variables.take(child); |
|
733 if (needsReverse) |
|
734 v *= -1; |
|
735 c->variables.insert(parallel, v); |
|
736 } |
|
737 } |
|
738 } |
|
739 |
|
740 // At this point we can identify that the parallel anchor is not feasible, e.g. one |
|
741 // anchor minimum size is bigger than the other anchor maximum size. |
|
742 *feasible = parallel->calculateSizeHints(); |
|
743 newAnchor = parallel; |
|
744 } |
|
745 |
|
746 g.createEdge(newAnchor->from, newAnchor->to, newAnchor); |
|
747 return newAnchor; |
|
748 } |
|
749 |
|
750 /*! |
|
751 \internal |
|
752 |
|
753 Takes the sequence of vertices described by (\a before, \a vertices, \a after) and removes |
|
754 all anchors connected to the vertices in \a vertices, returning one simplified anchor between |
|
755 \a before and \a after. |
|
756 |
|
757 Note that this function doesn't add the created anchor to the graph. This should be done by |
|
758 the caller. |
|
759 */ |
|
760 static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph, |
|
761 AnchorVertex *before, |
|
762 const QVector<AnchorVertex*> &vertices, |
|
763 AnchorVertex *after) |
|
764 { |
540 #if defined(QT_DEBUG) && 0 |
765 #if defined(QT_DEBUG) && 0 |
541 QString strVertices; |
766 QString strVertices; |
542 for (int i = 0; i < orderedVertices.count(); ++i) { |
767 for (int i = 0; i < vertices.count(); ++i) { |
543 strVertices += QString::fromAscii("%1 - ").arg(orderedVertices.at(i)->toString()); |
768 strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString()); |
544 } |
769 } |
545 QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString()); |
770 QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString()); |
546 qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); |
771 qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); |
547 #endif |
772 #endif |
548 |
773 |
549 SequentialAnchorData *sequence = new SequentialAnchorData; |
|
550 AnchorVertex *prev = before; |
774 AnchorVertex *prev = before; |
551 |
775 QVector<AnchorData *> edges; |
552 for (int i = 0; i <= orderedVertices.count(); ++i) { |
776 |
553 AnchorVertex *next = (i < orderedVertices.count()) ? orderedVertices.at(i) : after; |
777 // Take from the graph, the edges that will be simplificated |
|
778 for (int i = 0; i < vertices.count(); ++i) { |
|
779 AnchorVertex *next = vertices.at(i); |
554 AnchorData *ad = graph->takeEdge(prev, next); |
780 AnchorData *ad = graph->takeEdge(prev, next); |
555 Q_ASSERT(ad); |
781 Q_ASSERT(ad); |
556 sequence->m_edges.append(ad); |
782 edges.append(ad); |
557 prev = next; |
783 prev = next; |
558 } |
784 } |
559 |
785 |
560 sequence->setVertices(orderedVertices); |
786 // Take the last edge (not covered in the loop above) |
|
787 AnchorData *ad = graph->takeEdge(vertices.last(), after); |
|
788 Q_ASSERT(ad); |
|
789 edges.append(ad); |
|
790 |
|
791 // Create sequence |
|
792 SequentialAnchorData *sequence = new SequentialAnchorData(vertices, edges); |
561 sequence->from = before; |
793 sequence->from = before; |
562 sequence->to = after; |
794 sequence->to = after; |
563 |
795 |
564 sequence->refreshSizeHints_helper(0, false); |
796 sequence->calculateSizeHints(); |
565 |
797 |
566 // Note that since layout 'edges' can't be simplified away from |
798 return sequence; |
567 // the graph, it's safe to assume that if there's a layout |
|
568 // 'edge', it'll be in the boundaries of the sequence. |
|
569 sequence->isLayoutAnchor = (sequence->m_edges.first()->isLayoutAnchor |
|
570 || sequence->m_edges.last()->isLayoutAnchor); |
|
571 |
|
572 AnchorData *newAnchor = sequence; |
|
573 if (AnchorData *oldAnchor = graph->takeEdge(before, after)) { |
|
574 ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, sequence); |
|
575 parallel->isLayoutAnchor = (oldAnchor->isLayoutAnchor |
|
576 || sequence->isLayoutAnchor); |
|
577 parallel->refreshSizeHints_helper(0, false); |
|
578 newAnchor = parallel; |
|
579 } |
|
580 graph->createEdge(before, after, newAnchor); |
|
581 |
|
582 // True if we created a parallel anchor |
|
583 return newAnchor != sequence; |
|
584 } |
799 } |
585 |
800 |
586 /*! |
801 /*! |
587 \internal |
802 \internal |
588 |
803 |
615 - Then create a parallel anchor that holds the sequential anchor and the anchor just taken |
830 - Then create a parallel anchor that holds the sequential anchor and the anchor just taken |
616 out of the graph. |
831 out of the graph. |
617 2. Go to (1) |
832 2. Go to (1) |
618 3. Done |
833 3. Done |
619 |
834 |
|
835 When creating the parallel anchors, the algorithm might identify unfeasible situations. In this |
|
836 case the simplification process stops and returns false. Otherwise returns true. |
620 */ |
837 */ |
621 void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) |
838 bool QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) |
622 { |
839 { |
623 static bool noSimplification = !qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); |
840 if (items.isEmpty()) |
624 if (noSimplification || items.isEmpty()) |
841 return true; |
625 return; |
842 |
626 |
843 #if defined(QT_DEBUG) && 0 |
627 if (graphSimplified[orientation]) |
|
628 return; |
|
629 graphSimplified[orientation] = true; |
|
630 |
|
631 #if 0 |
|
632 qDebug("Simplifying Graph for %s", |
844 qDebug("Simplifying Graph for %s", |
633 orientation == Horizontal ? "Horizontal" : "Vertical"); |
845 orientation == Horizontal ? "Horizontal" : "Vertical"); |
|
846 |
|
847 static int count = 0; |
|
848 if (orientation == Horizontal) { |
|
849 count++; |
|
850 dumpGraph(QString::fromAscii("%1-full").arg(count)); |
|
851 } |
634 #endif |
852 #endif |
635 |
853 |
636 if (!graph[orientation].rootVertex()) |
854 // Vertex simplification |
637 return; |
855 if (!simplifyVertices(orientation)) { |
638 |
856 restoreVertices(orientation); |
|
857 return false; |
|
858 } |
|
859 |
|
860 // Anchor simplification |
639 bool dirty; |
861 bool dirty; |
|
862 bool feasible = true; |
640 do { |
863 do { |
641 dirty = simplifyGraphIteration(orientation); |
864 dirty = simplifyGraphIteration(orientation, &feasible); |
642 } while (dirty); |
865 } while (dirty && feasible); |
|
866 |
|
867 // Note that if we are not feasible, we fallback and make sure that the graph is fully restored |
|
868 if (!feasible) { |
|
869 restoreSimplifiedGraph(orientation); |
|
870 restoreVertices(orientation); |
|
871 return false; |
|
872 } |
|
873 |
|
874 #if defined(QT_DEBUG) && 0 |
|
875 dumpGraph(QString::fromAscii("%1-simplified-%2").arg(count).arg( |
|
876 QString::fromAscii(orientation == Horizontal ? "Horizontal" : "Vertical"))); |
|
877 #endif |
|
878 |
|
879 return true; |
|
880 } |
|
881 |
|
882 static AnchorVertex *replaceVertex_helper(AnchorData *data, AnchorVertex *oldV, AnchorVertex *newV) |
|
883 { |
|
884 AnchorVertex *other; |
|
885 if (data->from == oldV) { |
|
886 data->from = newV; |
|
887 other = data->to; |
|
888 } else { |
|
889 data->to = newV; |
|
890 other = data->from; |
|
891 } |
|
892 return other; |
|
893 } |
|
894 |
|
895 bool QGraphicsAnchorLayoutPrivate::replaceVertex(Orientation orientation, AnchorVertex *oldV, |
|
896 AnchorVertex *newV, const QList<AnchorData *> &edges) |
|
897 { |
|
898 Graph<AnchorVertex, AnchorData> &g = graph[orientation]; |
|
899 bool feasible = true; |
|
900 |
|
901 for (int i = 0; i < edges.count(); ++i) { |
|
902 AnchorData *ad = edges[i]; |
|
903 AnchorVertex *otherV = replaceVertex_helper(ad, oldV, newV); |
|
904 |
|
905 #if defined(QT_DEBUG) |
|
906 ad->name = QString::fromAscii("%1 --to--> %2").arg(ad->from->toString()).arg(ad->to->toString()); |
|
907 #endif |
|
908 |
|
909 bool newFeasible; |
|
910 AnchorData *newAnchor = addAnchorMaybeParallel(ad, &newFeasible); |
|
911 feasible &= newFeasible; |
|
912 |
|
913 if (newAnchor != ad) { |
|
914 // A parallel was created, we mark that in the list of anchors created by vertex |
|
915 // simplification. This is needed because we want to restore them in a separate step |
|
916 // from the restoration of anchor simplification. |
|
917 anchorsFromSimplifiedVertices[orientation].append(newAnchor); |
|
918 } |
|
919 |
|
920 g.takeEdge(oldV, otherV); |
|
921 } |
|
922 |
|
923 return feasible; |
|
924 } |
|
925 |
|
926 /*! |
|
927 \internal |
|
928 */ |
|
929 bool QGraphicsAnchorLayoutPrivate::simplifyVertices(Orientation orientation) |
|
930 { |
|
931 Q_Q(QGraphicsAnchorLayout); |
|
932 Graph<AnchorVertex, AnchorData> &g = graph[orientation]; |
|
933 |
|
934 // We'll walk through vertices |
|
935 QStack<AnchorVertex *> stack; |
|
936 stack.push(layoutFirstVertex[orientation]); |
|
937 QSet<AnchorVertex *> visited; |
|
938 |
|
939 while (!stack.isEmpty()) { |
|
940 AnchorVertex *v = stack.pop(); |
|
941 visited.insert(v); |
|
942 |
|
943 // Each adjacent of 'v' is a possible vertex to be merged. So we traverse all of |
|
944 // them. Since once a merge is made, we might add new adjacents, and we don't want to |
|
945 // pass two times through one adjacent. The 'index' is used to track our position. |
|
946 QList<AnchorVertex *> adjacents = g.adjacentVertices(v); |
|
947 int index = 0; |
|
948 |
|
949 while (index < adjacents.count()) { |
|
950 AnchorVertex *next = adjacents.at(index); |
|
951 index++; |
|
952 |
|
953 AnchorData *data = g.edgeData(v, next); |
|
954 const bool bothLayoutVertices = v->m_item == q && next->m_item == q; |
|
955 const bool zeroSized = !data->minSize && !data->maxSize; |
|
956 |
|
957 if (!bothLayoutVertices && zeroSized) { |
|
958 |
|
959 // Create a new vertex pair, note that we keep a list of those vertices so we can |
|
960 // easily process them when restoring the graph. |
|
961 AnchorVertexPair *newV = new AnchorVertexPair(v, next, data); |
|
962 simplifiedVertices[orientation].append(newV); |
|
963 |
|
964 // Collect the anchors of both vertices, the new vertex pair will take their place |
|
965 // in those anchors |
|
966 const QList<AnchorVertex *> &vAdjacents = g.adjacentVertices(v); |
|
967 const QList<AnchorVertex *> &nextAdjacents = g.adjacentVertices(next); |
|
968 |
|
969 for (int i = 0; i < vAdjacents.count(); ++i) { |
|
970 AnchorVertex *adjacent = vAdjacents.at(i); |
|
971 if (adjacent != next) { |
|
972 AnchorData *ad = g.edgeData(v, adjacent); |
|
973 newV->m_firstAnchors.append(ad); |
|
974 } |
|
975 } |
|
976 |
|
977 for (int i = 0; i < nextAdjacents.count(); ++i) { |
|
978 AnchorVertex *adjacent = nextAdjacents.at(i); |
|
979 if (adjacent != v) { |
|
980 AnchorData *ad = g.edgeData(next, adjacent); |
|
981 newV->m_secondAnchors.append(ad); |
|
982 |
|
983 // We'll also add new vertices to the adjacent list of the new 'v', to be |
|
984 // created as a vertex pair and replace the current one. |
|
985 if (!adjacents.contains(adjacent)) |
|
986 adjacents.append(adjacent); |
|
987 } |
|
988 } |
|
989 |
|
990 // ### merge this loop into the ones that calculated m_firstAnchors/m_secondAnchors? |
|
991 // Make newV take the place of v and next |
|
992 bool feasible = replaceVertex(orientation, v, newV, newV->m_firstAnchors); |
|
993 feasible &= replaceVertex(orientation, next, newV, newV->m_secondAnchors); |
|
994 |
|
995 // Update the layout vertex information if one of the vertices is a layout vertex. |
|
996 AnchorVertex *layoutVertex = 0; |
|
997 if (v->m_item == q) |
|
998 layoutVertex = v; |
|
999 else if (next->m_item == q) |
|
1000 layoutVertex = next; |
|
1001 |
|
1002 if (layoutVertex) { |
|
1003 // Layout vertices always have m_item == q... |
|
1004 newV->m_item = q; |
|
1005 changeLayoutVertex(orientation, layoutVertex, newV); |
|
1006 } |
|
1007 |
|
1008 g.takeEdge(v, next); |
|
1009 |
|
1010 // If a non-feasibility is found, we leave early and cancel the simplification |
|
1011 if (!feasible) |
|
1012 return false; |
|
1013 |
|
1014 v = newV; |
|
1015 visited.insert(newV); |
|
1016 |
|
1017 } else if (!visited.contains(next) && !stack.contains(next)) { |
|
1018 // If the adjacent is not fit for merge and it wasn't visited by the outermost |
|
1019 // loop, we add it to the stack. |
|
1020 stack.push(next); |
|
1021 } |
|
1022 } |
|
1023 } |
|
1024 |
|
1025 return true; |
643 } |
1026 } |
644 |
1027 |
645 /*! |
1028 /*! |
646 \internal |
1029 \internal |
647 |
1030 |
775 // Create a sequence for (beforeSequence, candidates, afterSequence). |
1143 // Create a sequence for (beforeSequence, candidates, afterSequence). |
776 // |
1144 // |
777 |
1145 |
778 // One restriction we have is to not simplify half of an anchor and let the other half |
1146 // One restriction we have is to not simplify half of an anchor and let the other half |
779 // unsimplified. So we remove center edges before and after the sequence. |
1147 // unsimplified. So we remove center edges before and after the sequence. |
780 if (beforeSequence->m_edge == centerEdge && beforeSequence->m_item == candidates.first()->m_item) { |
1148 const AnchorData *firstAnchor = g.edgeData(beforeSequence, candidates.first()); |
|
1149 if (firstAnchor->isCenterAnchor) { |
781 beforeSequence = candidates.first(); |
1150 beforeSequence = candidates.first(); |
782 candidates.remove(0); |
1151 candidates.remove(0); |
783 |
1152 |
784 // If there's not candidates to be simplified, leave. |
1153 // If there's not candidates to be simplified, leave. |
785 if (candidates.isEmpty()) |
1154 if (candidates.isEmpty()) |
786 continue; |
1155 continue; |
787 } |
1156 } |
788 |
1157 |
789 if (afterSequence->m_edge == centerEdge && afterSequence->m_item == candidates.last()->m_item) { |
1158 const AnchorData *lastAnchor = g.edgeData(candidates.last(), afterSequence); |
|
1159 if (lastAnchor->isCenterAnchor) { |
790 afterSequence = candidates.last(); |
1160 afterSequence = candidates.last(); |
791 candidates.remove(candidates.count() - 1); |
1161 candidates.remove(candidates.count() - 1); |
792 |
1162 |
793 if (candidates.isEmpty()) |
1163 if (candidates.isEmpty()) |
794 continue; |
1164 continue; |
795 } |
1165 } |
796 |
1166 |
797 // This function will remove the candidates from the graph and create one edge between |
1167 // |
798 // beforeSequence and afterSequence. This function returns true if the sequential |
1168 // Add the sequence to the graph. |
799 // simplification also caused a parallel simplification to be created. In this case we end |
1169 // |
800 // the iteration and start again (since all the visited state we have may be outdated). |
1170 |
801 if (simplifySequentialChunk(&g, beforeSequence, candidates, afterSequence)) |
1171 AnchorData *sequence = createSequence(&g, beforeSequence, candidates, afterSequence); |
|
1172 |
|
1173 // If 'beforeSequence' and 'afterSequence' already had an anchor between them, we'll |
|
1174 // create a parallel anchor between the new sequence and the old anchor. |
|
1175 bool newFeasible; |
|
1176 AnchorData *newAnchor = addAnchorMaybeParallel(sequence, &newFeasible); |
|
1177 |
|
1178 if (!newFeasible) { |
|
1179 *feasible = false; |
|
1180 return false; |
|
1181 } |
|
1182 |
|
1183 // When a new parallel anchor is create in the graph, we finish the iteration and return |
|
1184 // true to indicate a new iteration is needed. This happens because a parallel anchor |
|
1185 // changes the number of adjacents one vertex has, possibly opening up oportunities for |
|
1186 // building candidate lists (when adjacents == 2). |
|
1187 if (newAnchor != sequence) |
802 return true; |
1188 return true; |
803 |
1189 |
804 // If there was no parallel simplification, we'll keep walking the graph. So we clear the |
1190 // If there was no parallel simplification, we'll keep walking the graph. So we clear the |
805 // candidates list to start again. |
1191 // candidates list to start again. |
806 candidates.clear(); |
1192 candidates.clear(); |
807 } |
1193 } |
808 |
1194 |
809 return false; |
1195 return false; |
810 } |
1196 } |
811 |
1197 |
812 static void restoreSimplifiedAnchor(Graph<AnchorVertex, AnchorData> &g, |
1198 void QGraphicsAnchorLayoutPrivate::restoreSimplifiedAnchor(AnchorData *edge) |
813 AnchorData *edge, |
1199 { |
814 AnchorVertex *before, |
|
815 AnchorVertex *after) |
|
816 { |
|
817 Q_ASSERT(edge->type != AnchorData::Normal); |
|
818 #if 0 |
1200 #if 0 |
819 static const char *anchortypes[] = {"Normal", |
1201 static const char *anchortypes[] = {"Normal", |
820 "Sequential", |
1202 "Sequential", |
821 "Parallel"}; |
1203 "Parallel"}; |
822 qDebug("Restoring %s edge.", anchortypes[int(edge->type)]); |
1204 qDebug("Restoring %s edge.", anchortypes[int(edge->type)]); |
823 #endif |
1205 #endif |
824 if (edge->type == AnchorData::Sequential) { |
1206 |
825 SequentialAnchorData* seqEdge = static_cast<SequentialAnchorData*>(edge); |
1207 Graph<AnchorVertex, AnchorData> &g = graph[edge->orientation]; |
826 // restore the sequential anchor |
1208 |
827 AnchorVertex *prev = before; |
1209 if (edge->type == AnchorData::Normal) { |
828 AnchorVertex *last = after; |
1210 g.createEdge(edge->from, edge->to, edge); |
829 if (edge->from != prev) |
1211 |
830 qSwap(last, prev); |
1212 } else if (edge->type == AnchorData::Sequential) { |
831 |
1213 SequentialAnchorData *sequence = static_cast<SequentialAnchorData *>(edge); |
832 for (int i = 0; i < seqEdge->m_edges.count(); ++i) { |
1214 |
833 AnchorVertex *v1 = (i < seqEdge->m_children.count()) ? seqEdge->m_children.at(i) : last; |
1215 for (int i = 0; i < sequence->m_edges.count(); ++i) { |
834 AnchorData *data = seqEdge->m_edges.at(i); |
1216 AnchorData *data = sequence->m_edges.at(i); |
835 if (data->type != AnchorData::Normal) { |
1217 restoreSimplifiedAnchor(data); |
836 restoreSimplifiedAnchor(g, data, prev, v1); |
1218 } |
837 } else { |
1219 |
838 g.createEdge(prev, v1, data); |
1220 delete sequence; |
839 } |
1221 |
840 prev = v1; |
|
841 } |
|
842 } else if (edge->type == AnchorData::Parallel) { |
1222 } else if (edge->type == AnchorData::Parallel) { |
843 ParallelAnchorData* parallelEdge = static_cast<ParallelAnchorData*>(edge); |
1223 |
844 AnchorData *parallelEdges[2] = {parallelEdge->firstEdge, |
1224 // Skip parallel anchors that were created by vertex simplification, they will be processed |
845 parallelEdge->secondEdge}; |
1225 // later, when restoring vertex simplification. |
846 for (int i = 0; i < 2; ++i) { |
1226 // ### we could improve this check bit having a bit inside 'edge' |
847 AnchorData *data = parallelEdges[i]; |
1227 if (anchorsFromSimplifiedVertices[edge->orientation].contains(edge)) |
848 if (data->type == AnchorData::Normal) { |
1228 return; |
849 g.createEdge(before, after, data); |
1229 |
850 } else { |
1230 ParallelAnchorData* parallel = static_cast<ParallelAnchorData*>(edge); |
851 restoreSimplifiedAnchor(g, data, before, after); |
1231 restoreSimplifiedConstraints(parallel); |
852 } |
1232 |
853 } |
1233 // ### Because of the way parallel anchors are created in the anchor simplification |
|
1234 // algorithm, we know that one of these will be a sequence, so it'll be safe if the other |
|
1235 // anchor create an edge between the same vertices as the parallel. |
|
1236 Q_ASSERT(parallel->firstEdge->type == AnchorData::Sequential |
|
1237 || parallel->secondEdge->type == AnchorData::Sequential); |
|
1238 restoreSimplifiedAnchor(parallel->firstEdge); |
|
1239 restoreSimplifiedAnchor(parallel->secondEdge); |
|
1240 |
|
1241 delete parallel; |
|
1242 } |
|
1243 } |
|
1244 |
|
1245 void QGraphicsAnchorLayoutPrivate::restoreSimplifiedConstraints(ParallelAnchorData *parallel) |
|
1246 { |
|
1247 if (!parallel->isCenterAnchor) |
|
1248 return; |
|
1249 |
|
1250 for (int i = 0; i < parallel->m_firstConstraints.count(); ++i) { |
|
1251 QSimplexConstraint *c = parallel->m_firstConstraints.at(i); |
|
1252 qreal v = c->variables[parallel]; |
|
1253 c->variables.remove(parallel); |
|
1254 c->variables.insert(parallel->firstEdge, v); |
|
1255 } |
|
1256 |
|
1257 // When restoring, we might have to revert constraints back. See comments on |
|
1258 // addAnchorMaybeParallel(). |
|
1259 const bool needsReverse = !parallel->secondForward(); |
|
1260 |
|
1261 for (int i = 0; i < parallel->m_secondConstraints.count(); ++i) { |
|
1262 QSimplexConstraint *c = parallel->m_secondConstraints.at(i); |
|
1263 qreal v = c->variables[parallel]; |
|
1264 if (needsReverse) |
|
1265 v *= -1; |
|
1266 c->variables.remove(parallel); |
|
1267 c->variables.insert(parallel->secondEdge, v); |
854 } |
1268 } |
855 } |
1269 } |
856 |
1270 |
857 void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation) |
1271 void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation) |
858 { |
1272 { |
859 if (!graphSimplified[orientation]) |
|
860 return; |
|
861 graphSimplified[orientation] = false; |
|
862 |
|
863 #if 0 |
1273 #if 0 |
864 qDebug("Restoring Simplified Graph for %s", |
1274 qDebug("Restoring Simplified Graph for %s", |
865 orientation == Horizontal ? "Horizontal" : "Vertical"); |
1275 orientation == Horizontal ? "Horizontal" : "Vertical"); |
866 #endif |
1276 #endif |
867 |
1277 |
|
1278 // Restore anchor simplification |
868 Graph<AnchorVertex, AnchorData> &g = graph[orientation]; |
1279 Graph<AnchorVertex, AnchorData> &g = graph[orientation]; |
869 |
|
870 QList<QPair<AnchorVertex*, AnchorVertex*> > connections = g.connections(); |
1280 QList<QPair<AnchorVertex*, AnchorVertex*> > connections = g.connections(); |
871 for (int i = 0; i < connections.count(); ++i) { |
1281 for (int i = 0; i < connections.count(); ++i) { |
872 AnchorVertex *v1 = connections.at(i).first; |
1282 AnchorVertex *v1 = connections.at(i).first; |
873 AnchorVertex *v2 = connections.at(i).second; |
1283 AnchorVertex *v2 = connections.at(i).second; |
874 AnchorData *edge = g.edgeData(v1, v2); |
1284 AnchorData *edge = g.edgeData(v1, v2); |
875 if (edge->type != AnchorData::Normal) { |
1285 |
876 AnchorData *oldEdge = g.takeEdge(v1, v2); |
1286 // We restore only sequential anchors and parallels that were not created by |
877 restoreSimplifiedAnchor(g, edge, v1, v2); |
1287 // vertex simplification. |
878 delete oldEdge; |
1288 if (edge->type == AnchorData::Sequential |
879 } |
1289 || (edge->type == AnchorData::Parallel && |
880 } |
1290 !anchorsFromSimplifiedVertices[orientation].contains(edge))) { |
|
1291 |
|
1292 g.takeEdge(v1, v2); |
|
1293 restoreSimplifiedAnchor(edge); |
|
1294 } |
|
1295 } |
|
1296 |
|
1297 restoreVertices(orientation); |
|
1298 } |
|
1299 |
|
1300 void QGraphicsAnchorLayoutPrivate::restoreVertices(Orientation orientation) |
|
1301 { |
|
1302 Q_Q(QGraphicsAnchorLayout); |
|
1303 |
|
1304 Graph<AnchorVertex, AnchorData> &g = graph[orientation]; |
|
1305 QList<AnchorVertexPair *> &toRestore = simplifiedVertices[orientation]; |
|
1306 |
|
1307 // Since we keep a list of parallel anchors and vertices that were created during vertex |
|
1308 // simplification, we can now iterate on those lists instead of traversing the graph |
|
1309 // recursively. |
|
1310 |
|
1311 // First, restore the constraints changed when we created parallel anchors. Note that this |
|
1312 // works at this point because the constraints doesn't depend on vertex information and at |
|
1313 // this point it's always safe to identify whether the second child is forward or backwards. |
|
1314 // In the next step, we'll change the anchors vertices so that would not be possible anymore. |
|
1315 QList<AnchorData *> ¶llelAnchors = anchorsFromSimplifiedVertices[orientation]; |
|
1316 |
|
1317 for (int i = parallelAnchors.count() - 1; i >= 0; --i) { |
|
1318 ParallelAnchorData *parallel = static_cast<ParallelAnchorData *>(parallelAnchors.at(i)); |
|
1319 restoreSimplifiedConstraints(parallel); |
|
1320 } |
|
1321 |
|
1322 // Then, we will restore the vertices in the inverse order of creation, this way we ensure that |
|
1323 // the vertex being restored was not wrapped by another simplification. |
|
1324 for (int i = toRestore.count() - 1; i >= 0; --i) { |
|
1325 AnchorVertexPair *pair = toRestore.at(i); |
|
1326 QList<AnchorVertex *> adjacents = g.adjacentVertices(pair); |
|
1327 |
|
1328 // Restore the removed edge, this will also restore both vertices 'first' and 'second' to |
|
1329 // the graph structure. |
|
1330 AnchorVertex *first = pair->m_first; |
|
1331 AnchorVertex *second = pair->m_second; |
|
1332 g.createEdge(first, second, pair->m_removedAnchor); |
|
1333 |
|
1334 // Restore the anchors for the first child vertex |
|
1335 for (int j = 0; j < pair->m_firstAnchors.count(); ++j) { |
|
1336 AnchorData *ad = pair->m_firstAnchors.at(j); |
|
1337 Q_ASSERT(ad->from == pair || ad->to == pair); |
|
1338 |
|
1339 replaceVertex_helper(ad, pair, first); |
|
1340 g.createEdge(ad->from, ad->to, ad); |
|
1341 } |
|
1342 |
|
1343 // Restore the anchors for the second child vertex |
|
1344 for (int j = 0; j < pair->m_secondAnchors.count(); ++j) { |
|
1345 AnchorData *ad = pair->m_secondAnchors.at(j); |
|
1346 Q_ASSERT(ad->from == pair || ad->to == pair); |
|
1347 |
|
1348 replaceVertex_helper(ad, pair, second); |
|
1349 g.createEdge(ad->from, ad->to, ad); |
|
1350 } |
|
1351 |
|
1352 for (int j = 0; j < adjacents.count(); ++j) { |
|
1353 g.takeEdge(pair, adjacents.at(j)); |
|
1354 } |
|
1355 |
|
1356 // The pair simplified a layout vertex, so place back the correct vertex in the variable |
|
1357 // that track layout vertices |
|
1358 if (pair->m_item == q) { |
|
1359 AnchorVertex *layoutVertex = first->m_item == q ? first : second; |
|
1360 Q_ASSERT(layoutVertex->m_item == q); |
|
1361 changeLayoutVertex(orientation, pair, layoutVertex); |
|
1362 } |
|
1363 |
|
1364 delete pair; |
|
1365 } |
|
1366 qDeleteAll(parallelAnchors); |
|
1367 parallelAnchors.clear(); |
|
1368 toRestore.clear(); |
881 } |
1369 } |
882 |
1370 |
883 QGraphicsAnchorLayoutPrivate::Orientation |
1371 QGraphicsAnchorLayoutPrivate::Orientation |
884 QGraphicsAnchorLayoutPrivate::edgeOrientation(Qt::AnchorPoint edge) |
1372 QGraphicsAnchorLayoutPrivate::edgeOrientation(Qt::AnchorPoint edge) |
885 { |
1373 { |
903 // Horizontal |
1391 // Horizontal |
904 AnchorData *data = new AnchorData; |
1392 AnchorData *data = new AnchorData; |
905 addAnchor_helper(layout, Qt::AnchorLeft, layout, |
1393 addAnchor_helper(layout, Qt::AnchorLeft, layout, |
906 Qt::AnchorRight, data); |
1394 Qt::AnchorRight, data); |
907 data->maxSize = QWIDGETSIZE_MAX; |
1395 data->maxSize = QWIDGETSIZE_MAX; |
908 data->skipInPreferred = 1; |
1396 |
909 |
1397 // Save a reference to layout vertices |
910 // Set the Layout Left edge as the root of the horizontal graph. |
1398 layoutFirstVertex[Horizontal] = internalVertex(layout, Qt::AnchorLeft); |
911 AnchorVertex *v = internalVertex(layout, Qt::AnchorLeft); |
1399 layoutCentralVertex[Horizontal] = 0; |
912 graph[Horizontal].setRootVertex(v); |
1400 layoutLastVertex[Horizontal] = internalVertex(layout, Qt::AnchorRight); |
913 |
1401 |
914 // Vertical |
1402 // Vertical |
915 data = new AnchorData; |
1403 data = new AnchorData; |
916 addAnchor_helper(layout, Qt::AnchorTop, layout, |
1404 addAnchor_helper(layout, Qt::AnchorTop, layout, |
917 Qt::AnchorBottom, data); |
1405 Qt::AnchorBottom, data); |
918 data->maxSize = QWIDGETSIZE_MAX; |
1406 data->maxSize = QWIDGETSIZE_MAX; |
919 data->skipInPreferred = 1; |
1407 |
920 |
1408 // Save a reference to layout vertices |
921 // Set the Layout Top edge as the root of the vertical graph. |
1409 layoutFirstVertex[Vertical] = internalVertex(layout, Qt::AnchorTop); |
922 v = internalVertex(layout, Qt::AnchorTop); |
1410 layoutCentralVertex[Vertical] = 0; |
923 graph[Vertical].setRootVertex(v); |
1411 layoutLastVertex[Vertical] = internalVertex(layout, Qt::AnchorBottom); |
924 } |
1412 } |
925 |
1413 |
926 void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() |
1414 void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() |
927 { |
1415 { |
928 Q_Q(QGraphicsAnchorLayout); |
1416 Q_Q(QGraphicsAnchorLayout); |
929 |
1417 |
930 Q_ASSERT(internalVertex(q, Qt::AnchorHorizontalCenter) == NULL); |
1418 Q_ASSERT(!internalVertex(q, Qt::AnchorHorizontalCenter)); |
931 Q_ASSERT(internalVertex(q, Qt::AnchorVerticalCenter) == NULL); |
1419 Q_ASSERT(!internalVertex(q, Qt::AnchorVerticalCenter)); |
932 |
1420 |
933 removeAnchor_helper(internalVertex(q, Qt::AnchorLeft), |
1421 removeAnchor_helper(internalVertex(q, Qt::AnchorLeft), |
934 internalVertex(q, Qt::AnchorRight)); |
1422 internalVertex(q, Qt::AnchorRight)); |
935 removeAnchor_helper(internalVertex(q, Qt::AnchorTop), |
1423 removeAnchor_helper(internalVertex(q, Qt::AnchorTop), |
936 internalVertex(q, Qt::AnchorBottom)); |
1424 internalVertex(q, Qt::AnchorBottom)); |
937 } |
1425 } |
938 |
1426 |
939 void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) |
1427 void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) |
940 { |
1428 { |
941 Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]); |
|
942 |
|
943 items.append(item); |
1429 items.append(item); |
944 |
1430 |
945 // Create horizontal and vertical internal anchors for the item and |
1431 // Create horizontal and vertical internal anchors for the item and |
946 // refresh its size hint / policy values. |
1432 // refresh its size hint / policy values. |
947 AnchorData *data = new AnchorData; |
1433 AnchorData *data = new AnchorData; |
948 addAnchor_helper(item, Qt::AnchorLeft, item, Qt::AnchorRight, data); |
1434 addAnchor_helper(item, Qt::AnchorLeft, item, Qt::AnchorRight, data); |
949 data->refreshSizeHints(0); // 0 = effectiveSpacing, will not be used |
1435 data->refreshSizeHints(); |
950 |
1436 |
951 data = new AnchorData; |
1437 data = new AnchorData; |
952 addAnchor_helper(item, Qt::AnchorTop, item, Qt::AnchorBottom, data); |
1438 addAnchor_helper(item, Qt::AnchorTop, item, Qt::AnchorBottom, data); |
953 data->refreshSizeHints(0); // 0 = effectiveSpacing, will not be used |
1439 data->refreshSizeHints(); |
954 } |
1440 } |
955 |
1441 |
956 /*! |
1442 /*! |
957 \internal |
1443 \internal |
958 |
1444 |
1206 // Right ? 0 0 |
1715 // Right ? 0 0 |
1207 if (firstItem == q |
1716 if (firstItem == q |
1208 || secondItem == q |
1717 || secondItem == q |
1209 || pickEdge(firstEdge, Horizontal) == Qt::AnchorHorizontalCenter |
1718 || pickEdge(firstEdge, Horizontal) == Qt::AnchorHorizontalCenter |
1210 || oppositeEdge(firstEdge) != secondEdge) { |
1719 || oppositeEdge(firstEdge) != secondEdge) { |
1211 data->setPreferredSize(0); |
1720 graphicsAnchor->setSpacing(0); |
1212 } else { |
1721 } else { |
1213 data->unsetSize(); |
1722 graphicsAnchor->unsetSpacing(); |
1214 } |
1723 } |
1215 addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); |
1724 } |
1216 |
1725 |
1217 } else if (*spacing >= 0) { |
1726 return graphicsAnchor; |
1218 data->setPreferredSize(*spacing); |
1727 } |
1219 addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); |
1728 |
1220 |
1729 /* |
1221 } else { |
1730 \internal |
1222 data->setPreferredSize(-*spacing); |
1731 |
1223 addAnchor_helper(secondItem, secondEdge, firstItem, firstEdge, data); |
1732 This method adds an AnchorData to the internal graph. It is responsible for doing |
1224 } |
1733 the boilerplate part of such task. |
1225 |
1734 |
1226 return acquireGraphicsAnchor(data); |
1735 If another AnchorData exists between the mentioned vertices, it is deleted and |
1227 } |
1736 the new one is inserted. |
1228 |
1737 */ |
1229 void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstItem, |
1738 void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstItem, |
1230 Qt::AnchorPoint firstEdge, |
1739 Qt::AnchorPoint firstEdge, |
1231 QGraphicsLayoutItem *secondItem, |
1740 QGraphicsLayoutItem *secondItem, |
1232 Qt::AnchorPoint secondEdge, |
1741 Qt::AnchorPoint secondEdge, |
1233 AnchorData *data) |
1742 AnchorData *data) |
1234 { |
1743 { |
1235 Q_Q(QGraphicsAnchorLayout); |
1744 Q_Q(QGraphicsAnchorLayout); |
1236 |
1745 |
1237 // Guarantee that the graph is no simplified when adding this anchor, |
1746 const Orientation orientation = edgeOrientation(firstEdge); |
1238 // anchor manipulation always happen in the full graph |
1747 |
1239 restoreSimplifiedGraph(edgeOrientation(firstEdge)); |
1748 // Create or increase the reference count for the related vertices. |
1240 |
|
1241 // Is the Vertex (firstItem, firstEdge) already represented in our |
|
1242 // internal structure? |
|
1243 AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge); |
1749 AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge); |
1244 AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge); |
1750 AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge); |
1245 |
1751 |
1246 // Remove previous anchor |
1752 // Remove previous anchor |
1247 // ### Could we update the existing edgeData rather than creating a new one? |
1753 if (graph[orientation].edgeData(v1, v2)) { |
1248 if (graph[edgeOrientation(firstEdge)].edgeData(v1, v2)) { |
|
1249 removeAnchor_helper(v1, v2); |
1754 removeAnchor_helper(v1, v2); |
1250 } |
1755 } |
|
1756 |
|
1757 // If its an internal anchor, set the associated item |
|
1758 if (firstItem == secondItem) |
|
1759 data->item = firstItem; |
|
1760 |
|
1761 data->orientation = orientation; |
1251 |
1762 |
1252 // Create a bi-directional edge in the sense it can be transversed both |
1763 // Create a bi-directional edge in the sense it can be transversed both |
1253 // from v1 or v2. "data" however is shared between the two references |
1764 // from v1 or v2. "data" however is shared between the two references |
1254 // so we still know that the anchor direction is from 1 to 2. |
1765 // so we still know that the anchor direction is from 1 to 2. |
1255 data->from = v1; |
1766 data->from = v1; |
1256 data->to = v2; |
1767 data->to = v2; |
1257 #ifdef QT_DEBUG |
1768 #ifdef QT_DEBUG |
1258 data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString()); |
1769 data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString()); |
1259 #endif |
1770 #endif |
1260 // Keep track of anchors that are connected to the layout 'edges' |
1771 // ### bit to track internal anchors, since inside AnchorData methods |
1261 data->isLayoutAnchor = (v1->m_item == q || v2->m_item == q); |
1772 // we don't have access to the 'q' pointer. |
1262 |
1773 data->isLayoutAnchor = (data->item == q); |
1263 graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data); |
1774 |
|
1775 graph[orientation].createEdge(v1, v2, data); |
1264 } |
1776 } |
1265 |
1777 |
1266 QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *firstItem, |
1778 QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *firstItem, |
1267 Qt::AnchorPoint firstEdge, |
1779 Qt::AnchorPoint firstEdge, |
1268 QGraphicsLayoutItem *secondItem, |
1780 QGraphicsLayoutItem *secondItem, |
1269 Qt::AnchorPoint secondEdge) |
1781 Qt::AnchorPoint secondEdge) |
1270 { |
1782 { |
1271 Orientation orient = edgeOrientation(firstEdge); |
1783 // Do not expose internal anchors |
1272 restoreSimplifiedGraph(orient); |
1784 if (firstItem == secondItem) |
1273 |
1785 return 0; |
|
1786 |
|
1787 const Orientation orientation = edgeOrientation(firstEdge); |
1274 AnchorVertex *v1 = internalVertex(firstItem, firstEdge); |
1788 AnchorVertex *v1 = internalVertex(firstItem, firstEdge); |
1275 AnchorVertex *v2 = internalVertex(secondItem, secondEdge); |
1789 AnchorVertex *v2 = internalVertex(secondItem, secondEdge); |
1276 |
1790 |
1277 QGraphicsAnchor *graphicsAnchor = 0; |
1791 QGraphicsAnchor *graphicsAnchor = 0; |
1278 |
1792 |
1279 AnchorData *data = graph[orient].edgeData(v1, v2); |
1793 AnchorData *data = graph[orientation].edgeData(v1, v2); |
1280 if (data) |
1794 if (data) { |
1281 graphicsAnchor = acquireGraphicsAnchor(data); |
1795 // We could use "acquireGraphicsAnchor" here, but to avoid a regression where |
|
1796 // an internal anchor was wrongly exposed, I want to ensure no new |
|
1797 // QGraphicsAnchor instances are created by this call. |
|
1798 // This assumption must hold because anchors are either user-created (and already |
|
1799 // have their public object created), or they are internal (and must not reach |
|
1800 // this point). |
|
1801 Q_ASSERT(data->graphicsAnchor); |
|
1802 graphicsAnchor = data->graphicsAnchor; |
|
1803 } |
1282 return graphicsAnchor; |
1804 return graphicsAnchor; |
1283 } |
1805 } |
1284 |
1806 |
1285 /*! |
1807 /*! |
1286 * \internal |
1808 * \internal |
1359 private methods. |
1885 private methods. |
1360 */ |
1886 */ |
1361 void QGraphicsAnchorLayoutPrivate::removeAnchor_helper(AnchorVertex *v1, AnchorVertex *v2) |
1887 void QGraphicsAnchorLayoutPrivate::removeAnchor_helper(AnchorVertex *v1, AnchorVertex *v2) |
1362 { |
1888 { |
1363 Q_ASSERT(v1 && v2); |
1889 Q_ASSERT(v1 && v2); |
1364 // Guarantee that the graph is no simplified when removing this anchor, |
|
1365 // anchor manipulation always happen in the full graph |
|
1366 Orientation o = edgeOrientation(v1->m_edge); |
|
1367 restoreSimplifiedGraph(o); |
|
1368 |
1890 |
1369 // Remove edge from graph |
1891 // Remove edge from graph |
|
1892 const Orientation o = edgeOrientation(v1->m_edge); |
1370 graph[o].removeEdge(v1, v2); |
1893 graph[o].removeEdge(v1, v2); |
1371 |
1894 |
1372 // Decrease vertices reference count (may trigger a deletion) |
1895 // Decrease vertices reference count (may trigger a deletion) |
1373 removeInternalVertex(v1->m_item, v1->m_edge); |
1896 removeInternalVertex(v1->m_item, v1->m_edge); |
1374 removeInternalVertex(v2->m_item, v2->m_edge); |
1897 removeInternalVertex(v2->m_item, v2->m_edge); |
1375 } |
|
1376 |
|
1377 /*! |
|
1378 \internal |
|
1379 Only called from outside. (calls invalidate()) |
|
1380 */ |
|
1381 void QGraphicsAnchorLayoutPrivate::setAnchorSize(AnchorData *data, const qreal *anchorSize) |
|
1382 { |
|
1383 Q_Q(QGraphicsAnchorLayout); |
|
1384 // ### we can avoid restoration if we really want to, but we would have to |
|
1385 // search recursively through all composite anchors |
|
1386 Q_ASSERT(data); |
|
1387 restoreSimplifiedGraph(edgeOrientation(data->from->m_edge)); |
|
1388 |
|
1389 QGraphicsLayoutItem *firstItem = data->from->m_item; |
|
1390 QGraphicsLayoutItem *secondItem = data->to->m_item; |
|
1391 Qt::AnchorPoint firstEdge = data->from->m_edge; |
|
1392 Qt::AnchorPoint secondEdge = data->to->m_edge; |
|
1393 |
|
1394 // Use heuristics to find out what the user meant with this anchor. |
|
1395 correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge); |
|
1396 if (data->from->m_item != firstItem) |
|
1397 qSwap(data->from, data->to); |
|
1398 |
|
1399 if (anchorSize) { |
|
1400 // ### The current implementation makes "setAnchorSize" behavior |
|
1401 // dependent on the argument order for cases where we have |
|
1402 // no heuristic. Ie. two widgets, same anchor point. |
|
1403 |
|
1404 // We cannot have negative sizes inside the graph. This would cause |
|
1405 // the simplex solver to fail because all simplex variables are |
|
1406 // positive by definition. |
|
1407 // "negative spacing" is handled by inverting the standard item order. |
|
1408 if (*anchorSize >= 0) { |
|
1409 data->setPreferredSize(*anchorSize); |
|
1410 } else { |
|
1411 data->setPreferredSize(-*anchorSize); |
|
1412 qSwap(data->from, data->to); |
|
1413 } |
|
1414 } else { |
|
1415 data->unsetSize(); |
|
1416 } |
|
1417 q->invalidate(); |
|
1418 } |
|
1419 |
|
1420 void QGraphicsAnchorLayoutPrivate::anchorSize(const AnchorData *data, |
|
1421 qreal *minSize, |
|
1422 qreal *prefSize, |
|
1423 qreal *maxSize) const |
|
1424 { |
|
1425 Q_ASSERT(minSize || prefSize || maxSize); |
|
1426 Q_ASSERT(data); |
|
1427 QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate *>(this); |
|
1428 that->restoreSimplifiedGraph(edgeOrientation(data->from->m_edge)); |
|
1429 |
|
1430 if (minSize) |
|
1431 *minSize = data->minSize; |
|
1432 if (prefSize) |
|
1433 *prefSize = data->prefSize; |
|
1434 if (maxSize) |
|
1435 *maxSize = data->maxSize; |
|
1436 } |
1898 } |
1437 |
1899 |
1438 AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item, |
1900 AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item, |
1439 Qt::AnchorPoint edge) |
1901 Qt::AnchorPoint edge) |
1440 { |
1902 { |
1604 */ |
2062 */ |
1605 void QGraphicsAnchorLayoutPrivate::calculateGraphs() |
2063 void QGraphicsAnchorLayoutPrivate::calculateGraphs() |
1606 { |
2064 { |
1607 if (!calculateGraphCacheDirty) |
2065 if (!calculateGraphCacheDirty) |
1608 return; |
2066 return; |
1609 |
|
1610 #if defined(QT_DEBUG) && 0 |
|
1611 static int count = 0; |
|
1612 count++; |
|
1613 dumpGraph(QString::fromAscii("%1-before").arg(count)); |
|
1614 #endif |
|
1615 |
|
1616 calculateGraphs(Horizontal); |
2067 calculateGraphs(Horizontal); |
1617 calculateGraphs(Vertical); |
2068 calculateGraphs(Vertical); |
1618 |
2069 calculateGraphCacheDirty = false; |
1619 #if defined(QT_DEBUG) && 0 |
|
1620 dumpGraph(QString::fromAscii("%1-after").arg(count)); |
|
1621 #endif |
|
1622 |
|
1623 calculateGraphCacheDirty = 0; |
|
1624 } |
2070 } |
1625 |
2071 |
1626 // ### Maybe getGraphParts could return the variables when traversing, at least |
2072 // ### Maybe getGraphParts could return the variables when traversing, at least |
1627 // for trunk... |
2073 // for trunk... |
1628 QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints) |
2074 QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints) |
1629 { |
2075 { |
1630 QSet<AnchorData *> variableSet; |
2076 QSet<AnchorData *> variableSet; |
1631 for (int i = 0; i < constraints.count(); ++i) { |
2077 for (int i = 0; i < constraints.count(); ++i) { |
1632 const QSimplexConstraint *c = constraints[i]; |
2078 const QSimplexConstraint *c = constraints.at(i); |
1633 foreach (QSimplexVariable *var, c->variables.keys()) { |
2079 foreach (QSimplexVariable *var, c->variables.keys()) { |
1634 variableSet += static_cast<AnchorData *>(var); |
2080 variableSet += static_cast<AnchorData *>(var); |
1635 } |
2081 } |
1636 } |
2082 } |
1637 return variableSet.toList(); |
2083 return variableSet.toList(); |
1638 } |
2084 } |
1639 |
2085 |
1640 /*! |
2086 /*! |
1641 \internal |
2087 \internal |
1642 |
2088 |
1643 Calculate graphs is the method that puts together all the helper routines |
2089 Calculate graphs is the method that puts together all the helper routines |
1644 so that the AnchorLayout can calculate the sizes of each item. |
2090 so that the AnchorLayout can calculate the sizes of each item. |
1645 |
2091 |
1646 In a nutshell it should do: |
2092 In a nutshell it should do: |
1647 |
2093 |
1648 1) Update anchor nominal sizes, that is, the size that each anchor would |
2094 1) Refresh anchor nominal sizes, that is, the size that each anchor would |
1649 have if no other restrictions applied. This is done by quering the |
2095 have if no other restrictions applied. This is done by quering the |
1650 layout style and the sizeHints of the items belonging to the layout. |
2096 layout style and the sizeHints of the items belonging to the layout. |
1651 |
2097 |
1652 2) Simplify the graph by grouping together parallel and sequential anchors |
2098 2) Simplify the graph by grouping together parallel and sequential anchors |
1653 into "group anchors". These have equivalent minimum, preferred and maximum |
2099 into "group anchors". These have equivalent minimum, preferred and maximum |
1654 sizeHints as the anchors they replace. |
2100 sizeHints as the anchors they replace. |
1655 |
2101 |
1656 3) Check if we got to a trivial case. In some cases, the whole graph can be |
2102 3) Check if we got to a trivial case. In some cases, the whole graph can be |
1657 simplified into a single anchor. If so, use this information. If not, |
2103 simplified into a single anchor. If so, use this information. If not, |
1658 then call the Simplex solver to calculate the anchors sizes. |
2104 then call the Simplex solver to calculate the anchors sizes. |
1659 |
2105 |
1660 4) Once the root anchors had its sizes calculated, propagate that to the |
2106 4) Once the root anchors had its sizes calculated, propagate that to the |
1661 anchors they represent. |
2107 anchors they represent. |
1662 */ |
2108 */ |
1663 void QGraphicsAnchorLayoutPrivate::calculateGraphs( |
2109 void QGraphicsAnchorLayoutPrivate::calculateGraphs( |
1664 QGraphicsAnchorLayoutPrivate::Orientation orientation) |
2110 QGraphicsAnchorLayoutPrivate::Orientation orientation) |
1665 { |
2111 { |
1666 Q_Q(QGraphicsAnchorLayout); |
2112 #if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) |
|
2113 lastCalculationUsedSimplex[orientation] = false; |
|
2114 #endif |
|
2115 |
|
2116 static bool simplificationEnabled = qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); |
|
2117 |
|
2118 // Reset the nominal sizes of each anchor based on the current item sizes |
|
2119 refreshAllSizeHints(orientation); |
1667 |
2120 |
1668 // Simplify the graph |
2121 // Simplify the graph |
1669 simplifyGraph(orientation); |
2122 if (simplificationEnabled && !simplifyGraph(orientation)) { |
1670 |
2123 qWarning("QGraphicsAnchorLayout: anchor setup is not feasible."); |
1671 // Reset the nominal sizes of each anchor based on the current item sizes |
2124 graphHasConflicts[orientation] = true; |
1672 setAnchorSizeHintsFromItems(orientation); |
2125 return; |
|
2126 } |
1673 |
2127 |
1674 // Traverse all graph edges and store the possible paths to each vertex |
2128 // Traverse all graph edges and store the possible paths to each vertex |
1675 findPaths(orientation); |
2129 findPaths(orientation); |
1676 |
2130 |
1677 // From the paths calculated above, extract the constraints that the current |
2131 // From the paths calculated above, extract the constraints that the current |
1752 if (needsSimplex) { |
2228 if (needsSimplex) { |
1753 |
2229 |
1754 QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); |
2230 QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); |
1755 QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints; |
2231 QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints; |
1756 |
2232 |
|
2233 shiftConstraints(allConstraints, g_offset); |
|
2234 |
1757 // Solve min and max size hints |
2235 // Solve min and max size hints |
1758 qreal min, max; |
2236 qreal min, max; |
1759 feasible = solveMinMax(allConstraints, path, &min, &max); |
2237 feasible = solveMinMax(allConstraints, path, &min, &max); |
1760 |
2238 |
1761 if (feasible) { |
2239 if (feasible) { |
1762 solvePreferred(allConstraints, variables); |
2240 solvePreferred(constraints, variables); |
1763 |
2241 |
1764 // Note that we don't include the sizeHintConstraints, since they |
2242 // Calculate and set the preferred size for the layout, |
1765 // have a different logic for solveExpanding(). |
|
1766 solveExpanding(constraints, variables); |
|
1767 |
|
1768 // Calculate and set the preferred and expanding sizes for the layout, |
|
1769 // from the edge sizes that were calculated above. |
2243 // from the edge sizes that were calculated above. |
1770 qreal pref(0.0); |
2244 qreal pref(0.0); |
1771 qreal expanding(0.0); |
|
1772 foreach (const AnchorData *ad, path.positives) { |
2245 foreach (const AnchorData *ad, path.positives) { |
1773 pref += ad->sizeAtPreferred; |
2246 pref += ad->sizeAtPreferred; |
1774 expanding += ad->sizeAtExpanding; |
|
1775 } |
2247 } |
1776 foreach (const AnchorData *ad, path.negatives) { |
2248 foreach (const AnchorData *ad, path.negatives) { |
1777 pref -= ad->sizeAtPreferred; |
2249 pref -= ad->sizeAtPreferred; |
1778 expanding -= ad->sizeAtExpanding; |
|
1779 } |
2250 } |
1780 |
2251 |
1781 sizeHints[orientation][Qt::MinimumSize] = min; |
2252 sizeHints[orientation][Qt::MinimumSize] = min; |
1782 sizeHints[orientation][Qt::PreferredSize] = pref; |
2253 sizeHints[orientation][Qt::PreferredSize] = pref; |
1783 sizeHints[orientation][Qt::MaximumSize] = max; |
2254 sizeHints[orientation][Qt::MaximumSize] = max; |
1784 sizeAtExpanding[orientation] = expanding; |
|
1785 } |
2255 } |
1786 |
2256 |
1787 qDeleteAll(sizeHintConstraints); |
2257 qDeleteAll(sizeHintConstraints); |
|
2258 shiftConstraints(constraints, -g_offset); |
1788 |
2259 |
1789 } else { |
2260 } else { |
1790 // No Simplex is necessary because the path was simplified all the way to a single |
2261 // No Simplex is necessary because the path was simplified all the way to a single |
1791 // anchor. |
2262 // anchor. |
1792 Q_ASSERT(path.positives.count() == 1); |
2263 Q_ASSERT(path.positives.count() == 1); |
1793 Q_ASSERT(path.negatives.count() == 0); |
2264 Q_ASSERT(path.negatives.count() == 0); |
1794 |
2265 |
1795 AnchorData *ad = path.positives.toList()[0]; |
2266 AnchorData *ad = path.positives.toList()[0]; |
1796 ad->sizeAtMinimum = ad->minSize; |
2267 ad->sizeAtMinimum = ad->minSize; |
1797 ad->sizeAtPreferred = ad->prefSize; |
2268 ad->sizeAtPreferred = ad->prefSize; |
1798 ad->sizeAtExpanding = ad->expSize; |
|
1799 ad->sizeAtMaximum = ad->maxSize; |
2269 ad->sizeAtMaximum = ad->maxSize; |
1800 |
2270 |
1801 sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; |
2271 sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; |
1802 sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; |
2272 sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; |
1803 sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; |
2273 sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; |
1804 sizeAtExpanding[orientation] = ad->sizeAtExpanding; |
|
1805 } |
2274 } |
1806 |
2275 |
1807 #if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) |
2276 #if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) |
1808 lastCalculationUsedSimplex[orientation] = needsSimplex; |
2277 lastCalculationUsedSimplex[orientation] = needsSimplex; |
1809 #endif |
2278 #endif |
1956 sizes, as specified in its size hints |
2422 sizes, as specified in its size hints |
1957 */ |
2423 */ |
1958 QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints( |
2424 QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints( |
1959 const QList<AnchorData *> &anchors) |
2425 const QList<AnchorData *> &anchors) |
1960 { |
2426 { |
|
2427 if (anchors.isEmpty()) |
|
2428 return QList<QSimplexConstraint *>(); |
|
2429 |
|
2430 // Look for the layout edge. That can be either the first half in case the |
|
2431 // layout is split in two, or the whole layout anchor. |
|
2432 Orientation orient = Orientation(anchors.first()->orientation); |
|
2433 AnchorData *layoutEdge = 0; |
|
2434 if (layoutCentralVertex[orient]) { |
|
2435 layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutCentralVertex[orient]); |
|
2436 } else { |
|
2437 layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutLastVertex[orient]); |
|
2438 } |
|
2439 |
|
2440 // If maxSize is less then "infinite", that means there are other anchors |
|
2441 // grouped together with this one. We can't ignore its maximum value so we |
|
2442 // set back the variable to NULL to prevent the continue condition from being |
|
2443 // satisfied in the loop below. |
|
2444 const qreal expectedMax = layoutCentralVertex[orient] ? QWIDGETSIZE_MAX / 2 : QWIDGETSIZE_MAX; |
|
2445 qreal actualMax; |
|
2446 if (layoutEdge->from == layoutFirstVertex[orient]) { |
|
2447 actualMax = layoutEdge->maxSize; |
|
2448 } else { |
|
2449 actualMax = -layoutEdge->minSize; |
|
2450 } |
|
2451 if (actualMax != expectedMax) { |
|
2452 layoutEdge = 0; |
|
2453 } |
|
2454 |
|
2455 // For each variable, create constraints based on size hints |
1961 QList<QSimplexConstraint *> anchorConstraints; |
2456 QList<QSimplexConstraint *> anchorConstraints; |
|
2457 bool unboundedProblem = true; |
1962 for (int i = 0; i < anchors.size(); ++i) { |
2458 for (int i = 0; i < anchors.size(); ++i) { |
|
2459 AnchorData *ad = anchors.at(i); |
|
2460 |
|
2461 // Anchors that have their size directly linked to another one don't need constraints |
|
2462 // For exammple, the second half of an item has exactly the same size as the first half |
|
2463 // thus constraining the latter is enough. |
|
2464 if (ad->dependency == AnchorData::Slave) |
|
2465 continue; |
|
2466 |
|
2467 // To use negative variables inside simplex, we shift them so the minimum negative value is |
|
2468 // mapped to zero before solving. To make sure that it works, we need to guarantee that the |
|
2469 // variables are all inside a certain boundary. |
|
2470 qreal boundedMin = qBound(-g_offset, ad->minSize, g_offset); |
|
2471 qreal boundedMax = qBound(-g_offset, ad->maxSize, g_offset); |
|
2472 |
|
2473 if ((boundedMin == boundedMax) || qFuzzyCompare(boundedMin, boundedMax)) { |
|
2474 QSimplexConstraint *c = new QSimplexConstraint; |
|
2475 c->variables.insert(ad, 1.0); |
|
2476 c->constant = boundedMin; |
|
2477 c->ratio = QSimplexConstraint::Equal; |
|
2478 anchorConstraints += c; |
|
2479 unboundedProblem = false; |
|
2480 } else { |
|
2481 QSimplexConstraint *c = new QSimplexConstraint; |
|
2482 c->variables.insert(ad, 1.0); |
|
2483 c->constant = boundedMin; |
|
2484 c->ratio = QSimplexConstraint::MoreOrEqual; |
|
2485 anchorConstraints += c; |
|
2486 |
|
2487 // We avoid adding restrictions to the layout internal anchors. That's |
|
2488 // to prevent unnecessary fair distribution from happening due to this |
|
2489 // artificial restriction. |
|
2490 if (ad == layoutEdge) |
|
2491 continue; |
|
2492 |
|
2493 c = new QSimplexConstraint; |
|
2494 c->variables.insert(ad, 1.0); |
|
2495 c->constant = boundedMax; |
|
2496 c->ratio = QSimplexConstraint::LessOrEqual; |
|
2497 anchorConstraints += c; |
|
2498 unboundedProblem = false; |
|
2499 } |
|
2500 } |
|
2501 |
|
2502 // If no upper boundary restriction was added, add one to avoid unbounded problem |
|
2503 if (unboundedProblem) { |
1963 QSimplexConstraint *c = new QSimplexConstraint; |
2504 QSimplexConstraint *c = new QSimplexConstraint; |
1964 c->variables.insert(anchors[i], 1.0); |
2505 c->variables.insert(layoutEdge, 1.0); |
1965 c->constant = anchors[i]->minSize; |
2506 // The maximum size that the layout can take |
1966 c->ratio = QSimplexConstraint::MoreOrEqual; |
2507 c->constant = g_offset; |
1967 anchorConstraints += c; |
|
1968 |
|
1969 c = new QSimplexConstraint; |
|
1970 c->variables.insert(anchors[i], 1.0); |
|
1971 c->constant = anchors[i]->maxSize; |
|
1972 c->ratio = QSimplexConstraint::LessOrEqual; |
2508 c->ratio = QSimplexConstraint::LessOrEqual; |
1973 anchorConstraints += c; |
2509 anchorConstraints += c; |
1974 } |
2510 } |
1975 |
2511 |
1976 return anchorConstraints; |
2512 return anchorConstraints; |
2248 |
2768 |
2249 QPair<Interval, qreal> result; |
2769 QPair<Interval, qreal> result; |
2250 result = getFactor(current, |
2770 result = getFactor(current, |
2251 sizeHints[orientation][Qt::MinimumSize], |
2771 sizeHints[orientation][Qt::MinimumSize], |
2252 sizeHints[orientation][Qt::PreferredSize], |
2772 sizeHints[orientation][Qt::PreferredSize], |
2253 sizeAtExpanding[orientation], |
2773 sizeHints[orientation][Qt::PreferredSize], |
|
2774 sizeHints[orientation][Qt::PreferredSize], |
2254 sizeHints[orientation][Qt::MaximumSize]); |
2775 sizeHints[orientation][Qt::MaximumSize]); |
2255 |
2776 |
2256 interpolationInterval[orientation] = result.first; |
2777 interpolationInterval[orientation] = result.first; |
2257 interpolationProgress[orientation] = result.second; |
2778 interpolationProgress[orientation] = result.second; |
2258 } |
2779 } |
2259 |
2780 |
2260 /*! |
2781 /*! |
2261 \internal |
2782 \internal |
2262 |
2783 |
2263 Calculate the current Edge size based on the current Layout size and the |
2784 Calculate the current Edge size based on the current Layout size and the |
2264 size the edge is supposed to have when the layout is at its: |
2785 size the edge is supposed to have when the layout is at its: |
2265 |
2786 |
2266 - minimum size, |
2787 - minimum size, |
2267 - preferred size, |
2788 - preferred size, |
2268 - size when all expanding anchors are expanded, |
2789 - maximum size. |
2269 - maximum size. |
2790 |
2270 |
2791 These three key values are calculated in advance using linear |
2271 These three key values are calculated in advance using linear |
2792 programming (more expensive) or the simplification algorithm, then |
2272 programming (more expensive) or the simplification algorithm, then |
2793 subsequential resizes of the parent layout require a simple |
2273 subsequential resizes of the parent layout require a simple |
2794 interpolation. |
2274 interpolation. |
2795 */ |
2275 |
2796 void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorData *edge) |
2276 If the edge is sequential or parallel, it's possible to have more |
2797 { |
2277 vertices to be initalized, so it calls specialized functions that |
2798 const Orientation orientation = Orientation(edge->orientation); |
2278 will recurse back to interpolateEdge(). |
|
2279 */ |
|
2280 void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, |
|
2281 AnchorData *edge, |
|
2282 Orientation orientation) |
|
2283 { |
|
2284 const QPair<Interval, qreal> factor(interpolationInterval[orientation], |
2799 const QPair<Interval, qreal> factor(interpolationInterval[orientation], |
2285 interpolationProgress[orientation]); |
2800 interpolationProgress[orientation]); |
2286 |
2801 |
2287 qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred, |
2802 qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred, |
2288 edge->sizeAtExpanding, edge->sizeAtMaximum); |
2803 edge->sizeAtPreferred, edge->sizeAtPreferred, |
|
2804 edge->sizeAtMaximum); |
2289 |
2805 |
2290 Q_ASSERT(edge->from == base || edge->to == base); |
2806 Q_ASSERT(edge->from == base || edge->to == base); |
2291 |
2807 |
2292 if (edge->from == base) |
2808 // Calculate the distance for the vertex opposite to the base |
|
2809 if (edge->from == base) { |
2293 edge->to->distance = base->distance + edgeDistance; |
2810 edge->to->distance = base->distance + edgeDistance; |
2294 else |
2811 } else { |
2295 edge->from->distance = base->distance - edgeDistance; |
2812 edge->from->distance = base->distance - edgeDistance; |
2296 |
2813 } |
2297 // Process child anchors |
|
2298 if (edge->type == AnchorData::Sequential) |
|
2299 interpolateSequentialEdges(edge->from, |
|
2300 static_cast<SequentialAnchorData *>(edge), |
|
2301 orientation); |
|
2302 else if (edge->type == AnchorData::Parallel) |
|
2303 interpolateParallelEdges(edge->from, |
|
2304 static_cast<ParallelAnchorData *>(edge), |
|
2305 orientation); |
|
2306 } |
|
2307 |
|
2308 void QGraphicsAnchorLayoutPrivate::interpolateParallelEdges( |
|
2309 AnchorVertex *base, ParallelAnchorData *data, Orientation orientation) |
|
2310 { |
|
2311 // In parallels the boundary vertices are already calculate, we |
|
2312 // just need to look for sequential groups inside, because only |
|
2313 // them may have new vertices associated. |
|
2314 |
|
2315 // First edge |
|
2316 if (data->firstEdge->type == AnchorData::Sequential) |
|
2317 interpolateSequentialEdges(base, |
|
2318 static_cast<SequentialAnchorData *>(data->firstEdge), |
|
2319 orientation); |
|
2320 else if (data->firstEdge->type == AnchorData::Parallel) |
|
2321 interpolateParallelEdges(base, |
|
2322 static_cast<ParallelAnchorData *>(data->firstEdge), |
|
2323 orientation); |
|
2324 |
|
2325 // Second edge |
|
2326 if (data->secondEdge->type == AnchorData::Sequential) |
|
2327 interpolateSequentialEdges(base, |
|
2328 static_cast<SequentialAnchorData *>(data->secondEdge), |
|
2329 orientation); |
|
2330 else if (data->secondEdge->type == AnchorData::Parallel) |
|
2331 interpolateParallelEdges(base, |
|
2332 static_cast<ParallelAnchorData *>(data->secondEdge), |
|
2333 orientation); |
|
2334 } |
|
2335 |
|
2336 void QGraphicsAnchorLayoutPrivate::interpolateSequentialEdges( |
|
2337 AnchorVertex *base, SequentialAnchorData *data, Orientation orientation) |
|
2338 { |
|
2339 AnchorVertex *prev = base; |
|
2340 |
|
2341 // ### I'm not sure whether this assumption is safe. If not, |
|
2342 // consider that m_edges.last() could be used instead (so |
|
2343 // at(0) would be the one to be treated specially). |
|
2344 Q_ASSERT(base == data->m_edges.at(0)->to || base == data->m_edges.at(0)->from); |
|
2345 |
|
2346 // Skip the last |
|
2347 for (int i = 0; i < data->m_edges.count() - 1; ++i) { |
|
2348 AnchorData *child = data->m_edges.at(i); |
|
2349 interpolateEdge(prev, child, orientation); |
|
2350 prev = child->to; |
|
2351 } |
|
2352 |
|
2353 // Treat the last specially, since we already calculated it's end |
|
2354 // vertex, so it's only interesting if it's a complex one |
|
2355 if (data->m_edges.last()->type != AnchorData::Normal) |
|
2356 interpolateEdge(prev, data->m_edges.last(), orientation); |
|
2357 } |
2814 } |
2358 |
2815 |
2359 bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> &constraints, |
2816 bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> &constraints, |
2360 GraphPath path, qreal *min, qreal *max) |
2817 GraphPath path, qreal *min, qreal *max) |
2361 { |
2818 { |
2369 objective.variables.insert(*iter, 1.0); |
2826 objective.variables.insert(*iter, 1.0); |
2370 |
2827 |
2371 for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter) |
2828 for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter) |
2372 objective.variables.insert(*iter, -1.0); |
2829 objective.variables.insert(*iter, -1.0); |
2373 |
2830 |
|
2831 const qreal objectiveOffset = (path.positives.count() - path.negatives.count()) * g_offset; |
2374 simplex.setObjective(&objective); |
2832 simplex.setObjective(&objective); |
2375 |
2833 |
2376 // Calculate minimum values |
2834 // Calculate minimum values |
2377 *min = simplex.solveMin(); |
2835 *min = simplex.solveMin() - objectiveOffset; |
2378 |
2836 |
2379 // Save sizeAtMinimum results |
2837 // Save sizeAtMinimum results |
2380 QList<QSimplexVariable *> variables = simplex.constraintsVariables(); |
2838 QList<AnchorData *> variables = getVariables(constraints); |
2381 for (int i = 0; i < variables.size(); ++i) { |
2839 for (int i = 0; i < variables.size(); ++i) { |
2382 AnchorData *ad = static_cast<AnchorData *>(variables[i]); |
2840 AnchorData *ad = static_cast<AnchorData *>(variables.at(i)); |
2383 Q_ASSERT(ad->result >= ad->minSize || qFuzzyCompare(ad->result, ad->minSize)); |
2841 ad->sizeAtMinimum = ad->result - g_offset; |
2384 ad->sizeAtMinimum = ad->result; |
|
2385 } |
2842 } |
2386 |
2843 |
2387 // Calculate maximum values |
2844 // Calculate maximum values |
2388 *max = simplex.solveMax(); |
2845 *max = simplex.solveMax() - objectiveOffset; |
2389 |
2846 |
2390 // Save sizeAtMaximum results |
2847 // Save sizeAtMaximum results |
2391 for (int i = 0; i < variables.size(); ++i) { |
2848 for (int i = 0; i < variables.size(); ++i) { |
2392 AnchorData *ad = static_cast<AnchorData *>(variables[i]); |
2849 AnchorData *ad = static_cast<AnchorData *>(variables.at(i)); |
2393 Q_ASSERT(ad->result <= ad->maxSize || qFuzzyCompare(ad->result, ad->maxSize)); |
2850 ad->sizeAtMaximum = ad->result - g_offset; |
2394 ad->sizeAtMaximum = ad->result; |
|
2395 } |
2851 } |
2396 } |
2852 } |
2397 return feasible; |
2853 return feasible; |
|
2854 } |
|
2855 |
|
2856 enum slackType { Grower = -1, Shrinker = 1 }; |
|
2857 static QPair<QSimplexVariable *, QSimplexConstraint *> createSlack(QSimplexConstraint *sizeConstraint, |
|
2858 qreal interval, slackType type) |
|
2859 { |
|
2860 QSimplexVariable *slack = new QSimplexVariable; |
|
2861 sizeConstraint->variables.insert(slack, type); |
|
2862 |
|
2863 QSimplexConstraint *limit = new QSimplexConstraint; |
|
2864 limit->variables.insert(slack, 1.0); |
|
2865 limit->ratio = QSimplexConstraint::LessOrEqual; |
|
2866 limit->constant = interval; |
|
2867 |
|
2868 return qMakePair(slack, limit); |
2398 } |
2869 } |
2399 |
2870 |
2400 bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint *> &constraints, |
2871 bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint *> &constraints, |
2401 const QList<AnchorData *> &variables) |
2872 const QList<AnchorData *> &variables) |
2402 { |
2873 { |
2417 |
2889 |
2418 // This will fill the values for the structural constraints |
2890 // This will fill the values for the structural constraints |
2419 // and we now fill the values for the slack constraints (one per variable), |
2891 // and we now fill the values for the slack constraints (one per variable), |
2420 // which have this form (the constant A_pref was set when creating the slacks): |
2892 // which have this form (the constant A_pref was set when creating the slacks): |
2421 // |
2893 // |
2422 // A + A_shrinker - A_grower = A_pref |
2894 // A + A_shrinker_hard + A_shrinker_soft - A_grower_hard - A_grower_soft = A_pref |
2423 // |
2895 // |
2424 for (int i = 0; i < variables.size(); ++i) { |
2896 for (int i = 0; i < variables.size(); ++i) { |
2425 AnchorData *ad = variables[i]; |
2897 AnchorData *ad = variables.at(i); |
2426 if (ad->skipInPreferred) |
2898 |
|
2899 // The layout original structure anchors are not relevant in preferred size calculation |
|
2900 if (ad->isLayoutAnchor) |
2427 continue; |
2901 continue; |
2428 |
2902 |
2429 QSimplexVariable *grower = new QSimplexVariable; |
2903 // By default, all variables are equal to their preferred size. If they have room to |
2430 QSimplexVariable *shrinker = new QSimplexVariable; |
2904 // grow or shrink, such flexibility will be added by the additional variables below. |
2431 QSimplexConstraint *c = new QSimplexConstraint; |
2905 QSimplexConstraint *sizeConstraint = new QSimplexConstraint; |
2432 c->variables.insert(ad, 1.0); |
2906 preferredConstraints += sizeConstraint; |
2433 c->variables.insert(shrinker, 1.0); |
2907 sizeConstraint->variables.insert(ad, 1.0); |
2434 c->variables.insert(grower, -1.0); |
2908 sizeConstraint->constant = ad->prefSize + g_offset; |
2435 c->constant = ad->prefSize; |
2909 |
2436 |
2910 // Can easily shrink |
2437 preferredConstraints += c; |
2911 QPair<QSimplexVariable *, QSimplexConstraint *> slack; |
2438 preferredVariables += grower; |
2912 const qreal softShrinkInterval = ad->prefSize - ad->minPrefSize; |
2439 preferredVariables += shrinker; |
2913 if (softShrinkInterval) { |
2440 |
2914 slack = createSlack(sizeConstraint, softShrinkInterval, Shrinker); |
2441 objective.variables.insert(grower, 1.0); |
2915 preferredVariables += slack.first; |
2442 objective.variables.insert(shrinker, variables.size()); |
2916 preferredConstraints += slack.second; |
2443 } |
2917 |
2444 |
2918 // Add to objective with ratio == 1 (soft) |
|
2919 objective.variables.insert(slack.first, 1.0); |
|
2920 } |
|
2921 |
|
2922 // Can easily grow |
|
2923 const qreal softGrowInterval = ad->maxPrefSize - ad->prefSize; |
|
2924 if (softGrowInterval) { |
|
2925 slack = createSlack(sizeConstraint, softGrowInterval, Grower); |
|
2926 preferredVariables += slack.first; |
|
2927 preferredConstraints += slack.second; |
|
2928 |
|
2929 // Add to objective with ratio == 1 (soft) |
|
2930 objective.variables.insert(slack.first, 1.0); |
|
2931 } |
|
2932 |
|
2933 // Can shrink if really necessary |
|
2934 const qreal hardShrinkInterval = ad->minPrefSize - ad->minSize; |
|
2935 if (hardShrinkInterval) { |
|
2936 slack = createSlack(sizeConstraint, hardShrinkInterval, Shrinker); |
|
2937 preferredVariables += slack.first; |
|
2938 preferredConstraints += slack.second; |
|
2939 |
|
2940 // Add to objective with ratio == N (hard) |
|
2941 objective.variables.insert(slack.first, variables.size()); |
|
2942 } |
|
2943 |
|
2944 // Can grow if really necessary |
|
2945 const qreal hardGrowInterval = ad->maxSize - ad->maxPrefSize; |
|
2946 if (hardGrowInterval) { |
|
2947 slack = createSlack(sizeConstraint, hardGrowInterval, Grower); |
|
2948 preferredVariables += slack.first; |
|
2949 preferredConstraints += slack.second; |
|
2950 |
|
2951 // Add to objective with ratio == N (hard) |
|
2952 objective.variables.insert(slack.first, variables.size()); |
|
2953 } |
|
2954 } |
2445 |
2955 |
2446 QSimplex *simplex = new QSimplex; |
2956 QSimplex *simplex = new QSimplex; |
2447 bool feasible = simplex->setConstraints(constraints + preferredConstraints); |
2957 bool feasible = simplex->setConstraints(constraints + preferredConstraints); |
2448 if (feasible) { |
2958 if (feasible) { |
2449 simplex->setObjective(&objective); |
2959 simplex->setObjective(&objective); |
2464 // Delete constraints and variables we created. |
2974 // Delete constraints and variables we created. |
2465 qDeleteAll(preferredConstraints); |
2975 qDeleteAll(preferredConstraints); |
2466 qDeleteAll(preferredVariables); |
2976 qDeleteAll(preferredVariables); |
2467 |
2977 |
2468 return feasible; |
2978 return feasible; |
2469 } |
|
2470 |
|
2471 /*! |
|
2472 \internal |
|
2473 Calculate the "expanding" keyframe |
|
2474 |
|
2475 This new keyframe sits between the already existing sizeAtPreferred and |
|
2476 sizeAtMaximum keyframes. Its goal is to modify the interpolation between |
|
2477 the latter as to respect the "expanding" size policy of some anchors. |
|
2478 |
|
2479 Previously all items would be subject to a linear interpolation between |
|
2480 sizeAtPreferred and sizeAtMaximum values. This will change now, the |
|
2481 expanding anchors will change their size before the others. To calculate |
|
2482 this keyframe we use the following logic: |
|
2483 |
|
2484 1) Ask each anchor for their desired expanding size (ad->expSize), this |
|
2485 value depends on the anchor expanding property in the following way: |
|
2486 |
|
2487 - Expanding normal anchors want to grow towards their maximum size |
|
2488 - Non-expanding normal anchors want to remain at their preferred size. |
|
2489 - Sequential anchors wants to grow towards a size that is calculated by: |
|
2490 summarizing it's child anchors, where it will use preferred size for non-expanding anchors |
|
2491 and maximum size for expanding anchors. |
|
2492 - Parallel anchors want to grow towards the smallest maximum size of all the expanding anchors. |
|
2493 |
|
2494 2) Clamp their desired values to the value they assume in the neighbour |
|
2495 keyframes (sizeAtPreferred and sizeAtExpanding) |
|
2496 |
|
2497 3) Run simplex with a setup that ensures the following: |
|
2498 |
|
2499 a. Anchors will change their value from their sizeAtPreferred towards |
|
2500 their sizeAtMaximum as much as required to ensure that ALL anchors |
|
2501 reach their respective "desired" expanding sizes. |
|
2502 |
|
2503 b. No anchors will change their value beyond what is NEEDED to satisfy |
|
2504 the requirement above. |
|
2505 |
|
2506 The final result is that, at the "expanding" keyframe expanding anchors |
|
2507 will grow and take with them all anchors that are parallel to them. |
|
2508 However, non-expanding anchors will remain at their preferred size unless |
|
2509 they are forced to grow by a parallel expanding anchor. |
|
2510 |
|
2511 Note: For anchors where the sizeAtPreferred is bigger than sizeAtMaximum, |
|
2512 the visual effect when the layout grows from its preferred size is |
|
2513 the following: Expanding anchors will keep their size while non |
|
2514 expanding ones will shrink. Only after non-expanding anchors have |
|
2515 shrinked all the way, the expanding anchors will start to shrink too. |
|
2516 */ |
|
2517 void QGraphicsAnchorLayoutPrivate::solveExpanding(const QList<QSimplexConstraint *> &constraints, |
|
2518 const QList<AnchorData *> &variables) |
|
2519 { |
|
2520 QList<QSimplexConstraint *> itemConstraints; |
|
2521 QSimplexConstraint *objective = new QSimplexConstraint; |
|
2522 bool hasExpanding = false; |
|
2523 |
|
2524 // Construct the simplex constraints and objective |
|
2525 for (int i = 0; i < variables.size(); ++i) { |
|
2526 // For each anchor |
|
2527 AnchorData *ad = variables[i]; |
|
2528 |
|
2529 // Clamp the desired expanding size |
|
2530 qreal upperBoundary = qMax(ad->sizeAtPreferred, ad->sizeAtMaximum); |
|
2531 qreal lowerBoundary = qMin(ad->sizeAtPreferred, ad->sizeAtMaximum); |
|
2532 qreal boundedExpSize = qBound(lowerBoundary, ad->expSize, upperBoundary); |
|
2533 |
|
2534 // Expanding anchors are those that want to move from their preferred size |
|
2535 if (boundedExpSize != ad->sizeAtPreferred) |
|
2536 hasExpanding = true; |
|
2537 |
|
2538 // Lock anchor between boundedExpSize and sizeAtMaximum (ensure 3.a) |
|
2539 if (boundedExpSize == ad->sizeAtMaximum) { |
|
2540 // The interval has only one possible value, we can use an "Equal" |
|
2541 // constraint and don't need to add this variable to the objective. |
|
2542 QSimplexConstraint *itemC = new QSimplexConstraint; |
|
2543 itemC->ratio = QSimplexConstraint::Equal; |
|
2544 itemC->variables.insert(ad, 1.0); |
|
2545 itemC->constant = boundedExpSize; |
|
2546 itemConstraints << itemC; |
|
2547 } else { |
|
2548 // Add MoreOrEqual and LessOrEqual constraints. |
|
2549 QSimplexConstraint *itemC = new QSimplexConstraint; |
|
2550 itemC->ratio = QSimplexConstraint::MoreOrEqual; |
|
2551 itemC->variables.insert(ad, 1.0); |
|
2552 itemC->constant = qMin(boundedExpSize, ad->sizeAtMaximum); |
|
2553 itemConstraints << itemC; |
|
2554 |
|
2555 itemC = new QSimplexConstraint; |
|
2556 itemC->ratio = QSimplexConstraint::LessOrEqual; |
|
2557 itemC->variables.insert(ad, 1.0); |
|
2558 itemC->constant = qMax(boundedExpSize, ad->sizeAtMaximum); |
|
2559 itemConstraints << itemC; |
|
2560 |
|
2561 // Create objective to avoid the anchors from moving away from |
|
2562 // the preferred size more than the needed amount. (ensure 3.b) |
|
2563 // The objective function is the distance between sizeAtPreferred |
|
2564 // and sizeAtExpanding, it will be minimized. |
|
2565 if (ad->sizeAtExpanding < ad->sizeAtMaximum) { |
|
2566 // Try to shrink this variable towards its sizeAtPreferred value |
|
2567 objective->variables.insert(ad, 1.0); |
|
2568 } else { |
|
2569 // Try to grow this variable towards its sizeAtPreferred value |
|
2570 objective->variables.insert(ad, -1.0); |
|
2571 } |
|
2572 } |
|
2573 } |
|
2574 |
|
2575 // Solve |
|
2576 if (hasExpanding == false) { |
|
2577 // If no anchors are expanding, we don't need to run the simplex |
|
2578 // Set all variables to their preferred size |
|
2579 for (int i = 0; i < variables.size(); ++i) { |
|
2580 variables[i]->sizeAtExpanding = variables[i]->sizeAtPreferred; |
|
2581 } |
|
2582 } else { |
|
2583 // Run simplex |
|
2584 QSimplex simplex; |
|
2585 |
|
2586 // Satisfy expanding (3.a) |
|
2587 bool feasible = simplex.setConstraints(constraints + itemConstraints); |
|
2588 Q_ASSERT(feasible); |
|
2589 |
|
2590 // Reduce damage (3.b) |
|
2591 simplex.setObjective(objective); |
|
2592 simplex.solveMin(); |
|
2593 |
|
2594 // Collect results |
|
2595 for (int i = 0; i < variables.size(); ++i) { |
|
2596 variables[i]->sizeAtExpanding = variables[i]->result; |
|
2597 } |
|
2598 } |
|
2599 |
|
2600 delete objective; |
|
2601 qDeleteAll(itemConstraints); |
|
2602 } |
2979 } |
2603 |
2980 |
2604 /*! |
2981 /*! |
2605 \internal |
2982 \internal |
2606 Returns true if there are no arrangement that satisfies all constraints. |
2983 Returns true if there are no arrangement that satisfies all constraints. |