summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Roard <nicolasroard@google.com>2018-06-12 03:38:12 -0700
committerNicolas Roard <nicolasroard@google.com>2018-06-12 03:38:12 -0700
commit8589ebbaf4d9409084407f7d51a6f5765c8c2d04 (patch)
treea1004e2d76a2fdba8485034e7640402bd9f029ff
parent6c6d76490f56ff2e73646eee5158efad5029240a (diff)
downloadsherpa-8589ebbaf4d9409084407f7d51a6f5765c8c2d04.tar.gz
Fixes various issues in complex chains
- ratio behavior - complex weights Fixes: 77991323 Test: added checks 203,204,205,206,207,208,209,210,211 add AdvancedChainTest::testComplexChainWeights() add AdvancedChainTest::testTooSmall() update ChainHeadTest Change-Id: Ie0613888f21195d2279a413c7bd331e83ec6d4f2
-rw-r--r--constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java10
-rw-r--r--solver/src/main/java/android/support/constraint/solver/ArrayRow.java34
-rw-r--r--solver/src/main/java/android/support/constraint/solver/LinearSystem.java7
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/Chain.java86
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ChainHead.java62
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java38
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java6
-rw-r--r--solver/src/test/java/android/support/constraint/solver/AdvancedChainTest.java128
-rw-r--r--solver/src/test/java/android/support/constraint/solver/ChainHeadTest.java8
9 files changed, 320 insertions, 59 deletions
diff --git a/constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java b/constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java
index d4f1809..de20bce 100644
--- a/constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java
+++ b/constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java
@@ -24,6 +24,7 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
+import android.support.constraint.solver.LinearSystem;
import android.support.constraint.solver.Metrics;
import android.support.constraint.solver.widgets.*;
import android.support.constraint.solver.widgets.Guideline;
@@ -856,6 +857,7 @@ public class ConstraintLayout extends ViewGroup {
((Placeholder) child).updatePreLayout(this);
}
}
+
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
ConstraintWidget widget = getViewWidget(child);
@@ -2255,13 +2257,13 @@ public class ConstraintLayout extends ViewGroup {
* The child's weight that we can use to distribute the available horizontal space
* in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT
*/
- public float horizontalWeight = 0;
+ public float horizontalWeight = UNSET;
/**
* The child's weight that we can use to distribute the available vertical space
* in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT
*/
- public float verticalWeight = 0;
+ public float verticalWeight = UNSET;
/**
* If the child is the start of a horizontal chain, this attribute will drive how
@@ -2818,11 +2820,11 @@ public class ConstraintLayout extends ViewGroup {
break;
}
case Table.LAYOUT_CONSTRAINT_HORIZONTAL_WEIGHT: {
- horizontalWeight = a.getFloat(attr, 0);
+ horizontalWeight = a.getFloat(attr, horizontalWeight);
break;
}
case Table.LAYOUT_CONSTRAINT_VERTICAL_WEIGHT: {
- verticalWeight = a.getFloat(attr, 0);
+ verticalWeight = a.getFloat(attr, verticalWeight);
break;
}
case Table.LAYOUT_CONSTRAINT_HORIZONTAL_CHAINSTYLE: {
diff --git a/solver/src/main/java/android/support/constraint/solver/ArrayRow.java b/solver/src/main/java/android/support/constraint/solver/ArrayRow.java
index 3647cad..c1599cf 100644
--- a/solver/src/main/java/android/support/constraint/solver/ArrayRow.java
+++ b/solver/src/main/java/android/support/constraint/solver/ArrayRow.java
@@ -212,33 +212,33 @@ public class ArrayRow implements LinearSystem.Row {
SolverVariable variableEndA,
SolverVariable variableStartB,
SolverVariable variableEndB) {
+ constantValue = 0;
if (totalWeights == 0 || (currentWeight == nextWeight)) {
// endA - startA == endB - startB
// 0 = startA - endA + endB - startB
- constantValue = 0;
variables.put(variableStartA, 1);
variables.put(variableEndA, -1);
variables.put(variableEndB, 1);
variables.put(variableStartB, -1);
} else {
if (currentWeight == 0) {
- currentWeight = epsilon;
- }
- if (nextWeight == 0) {
- nextWeight = epsilon;
+ variables.put(variableStartA, 1);
+ variables.put(variableEndA, -1);
+ } else if (nextWeight == 0) {
+ variables.put(variableStartB, 1);
+ variables.put(variableEndB, -1);
+ } else {
+ float cw = currentWeight / totalWeights;
+ float nw = nextWeight / totalWeights;
+ float w = cw / nw;
+
+ // endA - startA == w * (endB - startB)
+ // 0 = startA - endA + w * (endB - startB)
+ variables.put(variableStartA, 1);
+ variables.put(variableEndA, -1);
+ variables.put(variableEndB, w);
+ variables.put(variableStartB, -w);
}
-
- float cw = currentWeight / totalWeights;
- float nw = nextWeight / totalWeights;
- float w = cw / nw;
-
- // endA - startA == w * (endB - startB)
- // 0 = startA - endA + w * (endB - startB)
- constantValue = 0;
- variables.put(variableStartA, 1);
- variables.put(variableEndA, -1);
- variables.put(variableEndB, w);
- variables.put(variableStartB, -w);
}
return this;
}
diff --git a/solver/src/main/java/android/support/constraint/solver/LinearSystem.java b/solver/src/main/java/android/support/constraint/solver/LinearSystem.java
index fc43d1f..bbd810b 100644
--- a/solver/src/main/java/android/support/constraint/solver/LinearSystem.java
+++ b/solver/src/main/java/android/support/constraint/solver/LinearSystem.java
@@ -805,6 +805,10 @@ public class LinearSystem {
} else {
done = true;
}
+ if (tries > mNumColumns / 2) {
+ // failsafe -- tried too many times
+ done = true;
+ }
}
}
@@ -831,6 +835,9 @@ public class LinearSystem {
System.out.println("IMPOSSIBLE SYSTEM, WTF");
throw new Exception();
}
+ if (infeasibleSystem) {
+ return tries;
+ }
}
return tries;
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/Chain.java b/solver/src/main/java/android/support/constraint/solver/widgets/Chain.java
index f992d66..94a1324 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/Chain.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/Chain.java
@@ -20,6 +20,8 @@ import android.support.constraint.solver.ArrayRow;
import android.support.constraint.solver.LinearSystem;
import android.support.constraint.solver.SolverVariable;
+import java.util.ArrayList;
+
import static android.support.constraint.solver.widgets.ConstraintWidget.*;
/**
@@ -53,8 +55,12 @@ class Chain {
chainsSize = constraintWidgetContainer.mVerticalChainsSize;
chainsArray = constraintWidgetContainer.mVerticalChainsArray;
}
+
for (int i = 0; i < chainsSize; i++) {
ChainHead first = chainsArray[i];
+ // we have to make sure we define the ChainHead here, otherwise the values we use may not
+ // be correctly initialized (as we initialize them in the ConstraintWidget.addToSolver())
+ first.define();
if (constraintWidgetContainer.optimizeFor(Optimizer.OPTIMIZATION_CHAIN)) {
if (!Optimizer.applyChainOptimized(constraintWidgetContainer, system, orientation, offset, first)) {
applyChainConstraints(constraintWidgetContainer, system, orientation, offset, first);
@@ -187,43 +193,49 @@ class Chain {
}
// Now, let's apply the centering / spreading for matched constraints widgets
- if (firstMatchConstraintsWidget != null) {
- // TODO: we should not try to apply the constraints for weights = 0
- widget = firstMatchConstraintsWidget;
- while (widget != null) {
- next = widget.mListNextMatchConstraintsWidget[orientation];
- if (next != null) {
- float currentWeight = widget.mWeight[orientation];
- float nextWeight = next.mWeight[orientation];
- SolverVariable begin = widget.mListAnchors[offset].mSolverVariable;
- SolverVariable end = widget.mListAnchors[offset + 1].mSolverVariable;
- SolverVariable nextBegin = next.mListAnchors[offset].mSolverVariable;
- SolverVariable nextEnd = next.mListAnchors[offset + 1].mSolverVariable;
-
- boolean applyEquality;
- int currentDimensionDefault;
- int nextDimensionDefault;
- if (orientation == ConstraintWidget.HORIZONTAL) {
- currentDimensionDefault = widget.mMatchConstraintDefaultWidth;
- nextDimensionDefault = next.mMatchConstraintDefaultWidth;
- } else {
- currentDimensionDefault = widget.mMatchConstraintDefaultHeight;
- nextDimensionDefault = next.mMatchConstraintDefaultHeight;
+ ArrayList<ConstraintWidget> listMatchConstraints = chainHead.mWeightedMatchConstraintsWidgets;
+ if (listMatchConstraints != null) {
+ final int count = listMatchConstraints.size();
+ if (count > 1) {
+ ConstraintWidget lastMatch = null;
+ float lastWeight = 0;
+
+ if (chainHead.mHasUndefinedWeights && !chainHead.mHasComplexMatchWeights) {
+ totalWeights = chainHead.mWidgetsMatchCount;
+ }
+
+ for (int i = 0; i < count; i++) {
+ ConstraintWidget match = listMatchConstraints.get(i);
+ float currentWeight = match.mWeight[orientation];
+
+ if (currentWeight < 0) {
+ if (chainHead.mHasComplexMatchWeights) {
+ system.addEquality(match.mListAnchors[offset + 1].mSolverVariable,
+ match.mListAnchors[offset].mSolverVariable, 0, SolverVariable.STRENGTH_HIGHEST);
+ continue;
+ }
+ currentWeight = 1;
+ }
+ if (currentWeight == 0) {
+ system.addEquality(match.mListAnchors[offset + 1].mSolverVariable,
+ match.mListAnchors[offset].mSolverVariable, 0, SolverVariable.STRENGTH_FIXED);
+ continue;
}
- applyEquality = ((currentDimensionDefault == MATCH_CONSTRAINT_SPREAD)
- || (currentDimensionDefault == MATCH_CONSTRAINT_RATIO)) &&
- ((nextDimensionDefault == MATCH_CONSTRAINT_SPREAD)
- || (nextDimensionDefault == MATCH_CONSTRAINT_RATIO));
- if (applyEquality) {
+ if (lastMatch != null) {
+ SolverVariable begin = lastMatch.mListAnchors[offset].mSolverVariable;
+ SolverVariable end = lastMatch.mListAnchors[offset + 1].mSolverVariable;
+ SolverVariable nextBegin = match.mListAnchors[offset].mSolverVariable;
+ SolverVariable nextEnd = match.mListAnchors[offset + 1].mSolverVariable;
ArrayRow row = system.createRow();
- row.createRowEqualMatchDimensions(currentWeight, totalWeights, nextWeight,
+ row.createRowEqualMatchDimensions(lastWeight, totalWeights, currentWeight,
begin, end, nextBegin, nextEnd);
system.addConstraint(row);
}
+ lastMatch = match;
+ lastWeight = currentWeight;
}
- widget = next;
}
}
@@ -264,6 +276,7 @@ class Chain {
// for chain spread, we need to add equal dimensions in between *visible* widgets
widget = firstVisibleWidget;
ConstraintWidget previousVisibleWidget = firstVisibleWidget;
+ boolean applyFixedEquality = chainHead.mWidgetsMatchCount > 0 && (chainHead.mWidgetsCount == chainHead.mWidgetsMatchCount);
while (widget != null) {
next = widget.mListNextVisibleWidget[orientation];
if (next != null || widget == lastVisibleWidget) {
@@ -309,9 +322,13 @@ class Chain {
if (widget == lastVisibleWidget) {
margin2 = lastVisibleWidget.mListAnchors[offset + 1].getMargin();
}
+ int strength = SolverVariable.STRENGTH_HIGHEST;
+ if (applyFixedEquality) {
+ strength = SolverVariable.STRENGTH_FIXED;
+ }
system.addCentering(begin, beginTarget, margin1, 0.5f,
beginNext, beginNextTarget, margin2,
- SolverVariable.STRENGTH_HIGHEST);
+ strength);
}
}
previousVisibleWidget = widget;
@@ -321,6 +338,7 @@ class Chain {
// for chain spread inside, we need to add equal dimensions in between *visible* widgets
widget = firstVisibleWidget;
ConstraintWidget previousVisibleWidget = firstVisibleWidget;
+ boolean applyFixedEquality = chainHead.mWidgetsMatchCount > 0 && (chainHead.mWidgetsCount == chainHead.mWidgetsMatchCount);
while (widget != null) {
next = widget.mListNextVisibleWidget[orientation];
if (widget != firstVisibleWidget && widget != lastVisibleWidget && next != null) {
@@ -355,10 +373,14 @@ class Chain {
if (previousVisibleWidget != null) {
beginMargin += previousVisibleWidget.mListAnchors[offset + 1].getMargin();
}
+ int strength = SolverVariable.STRENGTH_HIGHEST;
+ if (applyFixedEquality) {
+ strength = SolverVariable.STRENGTH_FIXED;
+ }
if (begin != null && beginTarget != null && beginNext != null && beginNextTarget != null) {
system.addCentering(begin, beginTarget, beginMargin, 0.5f,
beginNext, beginNextTarget, nextMargin,
- SolverVariable.STRENGTH_HIGHEST);
+ strength);
}
}
previousVisibleWidget = widget;
@@ -382,7 +404,7 @@ class Chain {
}
- // final centering, necessary if the chain is smaller than the available space...
+ // final centering, necessary if the chain is larger than the available space...
if ((isChainSpread || isChainSpreadInside) && firstVisibleWidget != null) {
ConstraintAnchor begin = firstVisibleWidget.mListAnchors[offset];
ConstraintAnchor end = lastVisibleWidget.mListAnchors[offset + 1];
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ChainHead.java b/solver/src/main/java/android/support/constraint/solver/widgets/ChainHead.java
index 70bfc38..df18d4a 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/ChainHead.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ChainHead.java
@@ -18,6 +18,12 @@ package android.support.constraint.solver.widgets;
import android.support.constraint.solver.widgets.ConstraintWidget.DimensionBehaviour;
+import java.util.ArrayList;
+
+import static android.support.constraint.solver.widgets.ConstraintWidget.MATCH_CONSTRAINT_PERCENT;
+import static android.support.constraint.solver.widgets.ConstraintWidget.MATCH_CONSTRAINT_RATIO;
+import static android.support.constraint.solver.widgets.ConstraintWidget.MATCH_CONSTRAINT_SPREAD;
+
/**
* Class to represent a chain by its main elements.
*/
@@ -30,9 +36,16 @@ public class ChainHead {
protected ConstraintWidget mHead;
protected ConstraintWidget mFirstMatchConstraintWidget;
protected ConstraintWidget mLastMatchConstraintWidget;
+ protected ArrayList<ConstraintWidget> mWeightedMatchConstraintsWidgets;
+ protected int mWidgetsCount;
+ protected int mWidgetsMatchCount;
protected float mTotalWeight = 0f;
private int mOrientation;
private boolean mIsRtl = false;
+ protected boolean mHasUndefinedWeights;
+ protected boolean mHasDefinedWeights;
+ protected boolean mHasComplexMatchWeights;
+ private boolean mDefined;
/**
* Initialize variables, then determine visible widgets, the head of chain and
@@ -46,7 +59,20 @@ public class ChainHead {
mFirst = first;
mOrientation = orientation;
mIsRtl = isRtl;
- defineChainProperties();
+ }
+
+ /**
+ * Returns true if the widget should be part of the match equality rules in the chain
+ *
+ * @param widget the widget to test
+ * @param orientation current orientation, HORIZONTAL or VERTICAL
+ * @return
+ */
+ static private boolean isMatchConstraintEqualityCandidate(ConstraintWidget widget, int orientation) {
+ return widget.getVisibility() != ConstraintWidget.GONE
+ && widget.mListDimensionBehaviors[orientation] == ConstraintWidget.DimensionBehaviour.MATCH_CONSTRAINT
+ && (widget.mResolvedMatchConstraintDefault[orientation] == MATCH_CONSTRAINT_SPREAD
+ || widget.mResolvedMatchConstraintDefault[orientation] == MATCH_CONSTRAINT_RATIO);
}
private void defineChainProperties(){
@@ -57,6 +83,7 @@ public class ChainHead {
ConstraintWidget next = mFirst;
boolean done = false;
while (!done) {
+ mWidgetsCount++;
widget.mListNextVisibleWidget[mOrientation] = null;
widget.mListNextMatchConstraintsWidget[mOrientation] = null;
if(widget.getVisibility() != ConstraintWidget.GONE) {
@@ -70,8 +97,28 @@ public class ChainHead {
mLastVisibleWidget = widget;
// Match constraint linked list.
- if(widget.mListDimensionBehaviors[mOrientation] == DimensionBehaviour.MATCH_CONSTRAINT){
- mTotalWeight += widget.mWeight[mOrientation];
+ if(widget.mListDimensionBehaviors[mOrientation] == DimensionBehaviour.MATCH_CONSTRAINT
+ && (widget.mResolvedMatchConstraintDefault[mOrientation] == MATCH_CONSTRAINT_SPREAD
+ || widget.mResolvedMatchConstraintDefault[mOrientation] == MATCH_CONSTRAINT_RATIO
+ || widget.mResolvedMatchConstraintDefault[mOrientation] == MATCH_CONSTRAINT_PERCENT)) {
+ mWidgetsMatchCount++;
+ float weight = widget.mWeight[mOrientation];
+ if (weight > 0) {
+ mTotalWeight += widget.mWeight[mOrientation];
+ }
+
+ if (isMatchConstraintEqualityCandidate(widget, mOrientation)) {
+ if (weight < 0) {
+ mHasUndefinedWeights = true;
+ } else {
+ mHasDefinedWeights = true;
+ }
+ if (mWeightedMatchConstraintsWidgets == null) {
+ mWeightedMatchConstraintsWidgets = new ArrayList<>();
+ }
+ mWeightedMatchConstraintsWidgets.add(widget);
+ }
+
if(mFirstMatchConstraintWidget == null){
mFirstMatchConstraintWidget = widget;
}
@@ -106,6 +153,8 @@ public class ChainHead {
}else{
mHead = mFirst;
}
+
+ mHasComplexMatchWeights = mHasDefinedWeights && mHasUndefinedWeights;
}
public ConstraintWidget getFirst() {
@@ -139,4 +188,11 @@ public class ChainHead {
public float getTotalWeight() {
return mTotalWeight;
}
+
+ public void define() {
+ if (!mDefined) {
+ defineChainProperties();
+ }
+ mDefined = true;
+ }
}
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java
index ed8bd32..271be33 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java
@@ -75,6 +75,8 @@ public class ConstraintWidget {
int mMatchConstraintDefaultWidth = MATCH_CONSTRAINT_SPREAD;
int mMatchConstraintDefaultHeight = MATCH_CONSTRAINT_SPREAD;
+ int[] mResolvedMatchConstraintDefault = new int[2];
+
int mMatchConstraintMinWidth = 0;
int mMatchConstraintMaxWidth = 0;
float mMatchConstraintPercentWidth = 1;
@@ -232,7 +234,7 @@ public class ConstraintWidget {
boolean mHorizontalChainFixedPosition;
boolean mVerticalChainFixedPosition;
- float[] mWeight = {0, 0};
+ float[] mWeight = { UNKNOWN, UNKNOWN};
protected ConstraintWidget[] mListNextMatchConstraintsWidget = {null, null};
protected ConstraintWidget[] mListNextVisibleWidget = {null, null};
@@ -283,8 +285,8 @@ public class ConstraintWidget {
mVerticalChainStyle = CHAIN_SPREAD;
mHorizontalChainFixedPosition = false;
mVerticalChainFixedPosition = false;
- mWeight[DIMENSION_HORIZONTAL] = 0;
- mWeight[DIMENSION_VERTICAL] = 0;
+ mWeight[DIMENSION_HORIZONTAL] = UNKNOWN;
+ mWeight[DIMENSION_VERTICAL] = UNKNOWN;
mHorizontalResolution = UNKNOWN;
mVerticalResolution = UNKNOWN;
mMaxDimension[HORIZONTAL] = Integer.MAX_VALUE;
@@ -676,6 +678,21 @@ public class ConstraintWidget {
}
/**
+ * Create all the system variables for this widget
+ * @hide
+ * @param system
+ */
+ public void createObjectVariables(LinearSystem system) {
+ SolverVariable left = system.createObjectVariable(mLeft);
+ SolverVariable top = system.createObjectVariable(mTop);
+ SolverVariable right = system.createObjectVariable(mRight);
+ SolverVariable bottom = system.createObjectVariable(mBottom);
+ if (mBaselineDistance > 0) {
+ SolverVariable baseline = system.createObjectVariable(mBaseline);
+ }
+ }
+
+ /**
* Returns a string representation of the ConstraintWidget
*
* @return string representation of the widget
@@ -2286,6 +2303,9 @@ public class ConstraintWidget {
}
}
+ mResolvedMatchConstraintDefault[HORIZONTAL] = matchConstraintDefaultWidth;
+ mResolvedMatchConstraintDefault[VERTICAL] = matchConstraintDefaultHeight;
+
boolean useHorizontalRatio = useRatio && (mResolvedDimensionRatioSide == HORIZONTAL
|| mResolvedDimensionRatioSide == UNKNOWN);
@@ -2701,6 +2721,18 @@ public class ConstraintWidget {
int top = system.getObjectVariableValue(mTop);
int right = system.getObjectVariableValue(mRight);
int bottom = system.getObjectVariableValue(mBottom);
+ int w = right - left;
+ int h = bottom - top;
+ if (w < 0 || h < 0
+ || left == Integer.MIN_VALUE || left == Integer.MAX_VALUE
+ || top == Integer.MIN_VALUE || top == Integer.MAX_VALUE
+ || right == Integer.MIN_VALUE || right == Integer.MAX_VALUE
+ || bottom == Integer.MIN_VALUE || bottom == Integer.MAX_VALUE) {
+ left = 0;
+ top = 0;
+ right = 0;
+ bottom = 0;
+ }
setFrame(left, top, right, bottom);
}
} \ No newline at end of file
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java
index 322aef9..42ab6f1 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java
@@ -356,6 +356,12 @@ public class ConstraintWidgetContainer extends WidgetContainer {
widget.setDebugSolverName(mSystem, widget.getDebugName());
}
}
+ } else {
+ createObjectVariables(mSystem);
+ for (int i = 0; i < count; i++) {
+ ConstraintWidget widget = mChildren.get(i);
+ widget.createObjectVariables(mSystem);
+ }
}
needsSolving = addChildrenToSolver(mSystem);
if (needsSolving) {
diff --git a/solver/src/test/java/android/support/constraint/solver/AdvancedChainTest.java b/solver/src/test/java/android/support/constraint/solver/AdvancedChainTest.java
index 79df53c..e9f2ff6 100644
--- a/solver/src/test/java/android/support/constraint/solver/AdvancedChainTest.java
+++ b/solver/src/test/java/android/support/constraint/solver/AdvancedChainTest.java
@@ -30,6 +30,134 @@ import static org.testng.Assert.assertEquals;
public class AdvancedChainTest {
@Test
+ public void testComplexChainWeights() {
+ ConstraintWidgetContainer root = new ConstraintWidgetContainer(0, 0, 800, 800);
+ ConstraintWidget A = new ConstraintWidget(100, 20);
+ ConstraintWidget B = new ConstraintWidget(100, 20);
+
+ root.setDebugSolverName(root.getSystem(), "root");
+ A.setDebugSolverName(root.getSystem(), "A");
+ B.setDebugSolverName(root.getSystem(), "B");
+
+ A.connect(Type.LEFT, root, Type.LEFT);
+ A.connect(Type.RIGHT, root, Type.RIGHT);
+ B.connect(Type.LEFT, root, Type.LEFT);
+ B.connect(Type.RIGHT, root, Type.RIGHT);
+
+ A.connect(Type.TOP, root, Type.TOP, 0);
+ A.connect(Type.BOTTOM, B, Type.TOP, 0);
+
+ B.connect(Type.TOP, A, Type.BOTTOM, 0);
+ B.connect(Type.BOTTOM, root, Type.BOTTOM, 0);
+
+ root.add(A);
+ root.add(B);
+
+ A.setHorizontalDimensionBehaviour(DimensionBehaviour.MATCH_CONSTRAINT);
+ A.setVerticalDimensionBehaviour(DimensionBehaviour.MATCH_CONSTRAINT);
+ B.setHorizontalDimensionBehaviour(DimensionBehaviour.MATCH_CONSTRAINT);
+ B.setVerticalDimensionBehaviour(DimensionBehaviour.MATCH_CONSTRAINT);
+
+ root.setOptimizationLevel(Optimizer.OPTIMIZATION_NONE);
+ root.layout();
+
+ System.out.println("root: " + root);
+ System.out.println("A: " + A);
+ System.out.println("B: " + B);
+
+ assertEquals(A.getWidth(), 800);
+ assertEquals(B.getWidth(), 800);
+ assertEquals(A.getHeight(), 400);
+ assertEquals(B.getHeight(), 400);
+ assertEquals(A.getTop(), 0);
+ assertEquals(B.getTop(), 400);
+
+ A.setDimensionRatio("16:3");
+
+ root.layout();
+
+ System.out.println("root: " + root);
+ System.out.println("A: " + A);
+ System.out.println("B: " + B);
+
+ assertEquals(A.getWidth(), 800);
+ assertEquals(B.getWidth(), 800);
+ assertEquals(A.getHeight(), 150);
+ assertEquals(B.getHeight(), 150);
+ assertEquals(A.getTop(), 167);
+ assertEquals(B.getTop(), 483);
+
+ B.setVerticalWeight(1);
+
+ root.layout();
+
+ System.out.println("root: " + root);
+ System.out.println("A: " + A);
+ System.out.println("B: " + B);
+
+ assertEquals(A.getWidth(), 800);
+ assertEquals(B.getWidth(), 800);
+ assertEquals(A.getHeight(), 150);
+ assertEquals(B.getHeight(), 650);
+ assertEquals(A.getTop(), 0);
+ assertEquals(B.getTop(), 150);
+
+ A.setVerticalWeight(1);
+
+ root.layout();
+
+ System.out.println("root: " + root);
+ System.out.println("A: " + A);
+ System.out.println("B: " + B);
+
+ assertEquals(A.getWidth(), 800);
+ assertEquals(B.getWidth(), 800);
+ assertEquals(A.getHeight(), 150);
+ assertEquals(B.getHeight(), 150);
+ assertEquals(A.getTop(), 167);
+ assertEquals(B.getTop(), 483);
+ }
+
+ @Test
+ public void testTooSmall() {
+ ConstraintWidgetContainer root = new ConstraintWidgetContainer(0, 0, 800, 800);
+ ConstraintWidget A = new ConstraintWidget(100, 20);
+ ConstraintWidget B = new ConstraintWidget(100, 20);
+ ConstraintWidget C = new ConstraintWidget(100, 20);
+
+ root.setDebugSolverName(root.getSystem(), "root");
+ A.setDebugSolverName(root.getSystem(), "A");
+ B.setDebugSolverName(root.getSystem(), "B");
+ C.setDebugSolverName(root.getSystem(), "C");
+
+ root.add(A);
+ root.add(B);
+ root.add(C);
+
+ A.connect(Type.LEFT, root, Type.LEFT);
+ A.connect(Type.TOP, root, Type.TOP);
+ A.connect(Type.BOTTOM, root, Type.BOTTOM);
+
+ B.connect(Type.LEFT, A, Type.RIGHT, 100);
+ C.connect(Type.LEFT, A, Type.RIGHT, 100);
+
+ B.connect(Type.TOP, A, Type.TOP);
+ B.connect(Type.BOTTOM, C, Type.TOP);
+ C.connect(Type.TOP, B, Type.BOTTOM);
+ C.connect(Type.BOTTOM, A, Type.BOTTOM);
+
+ root.setOptimizationLevel(Optimizer.OPTIMIZATION_NONE);
+ root.layout();
+
+ System.out.println("A: " + A);
+ System.out.println("B: " + B);
+ System.out.println("C: " + C);
+ assertEquals(A.getTop(), 390);
+ assertEquals(B.getTop(), 380);
+ assertEquals(C.getTop(), 400);
+ }
+
+ @Test
public void testChainWeights() {
ConstraintWidgetContainer root = new ConstraintWidgetContainer(0, 0, 800, 800);
ConstraintWidget A = new ConstraintWidget(100, 20);
diff --git a/solver/src/test/java/android/support/constraint/solver/ChainHeadTest.java b/solver/src/test/java/android/support/constraint/solver/ChainHeadTest.java
index f6c8e8a..13f14fa 100644
--- a/solver/src/test/java/android/support/constraint/solver/ChainHeadTest.java
+++ b/solver/src/test/java/android/support/constraint/solver/ChainHeadTest.java
@@ -53,6 +53,7 @@ public class ChainHeadTest {
C.connect(ConstraintAnchor.Type.TOP, root, ConstraintAnchor.Type.TOP);
ChainHead chainHead = new ChainHead(A, ConstraintWidget.HORIZONTAL, false);
+ chainHead.define();
assertEquals(chainHead.getHead(), A);
assertEquals(chainHead.getFirst(), A);
@@ -63,6 +64,7 @@ public class ChainHeadTest {
A.setVisibility(ConstraintWidget.GONE);
chainHead = new ChainHead(A, ConstraintWidget.HORIZONTAL, false);
+ chainHead.define();
assertEquals(chainHead.getHead(), A);
assertEquals(chainHead.getFirst(), A);
@@ -70,6 +72,7 @@ public class ChainHeadTest {
chainHead = new ChainHead(A, ConstraintWidget.HORIZONTAL, true);
+ chainHead.define();
assertEquals(chainHead.getHead(), C);
assertEquals(chainHead.getFirst(), A);
@@ -103,6 +106,7 @@ public class ChainHeadTest {
C.connect(ConstraintAnchor.Type.LEFT, root, ConstraintAnchor.Type.LEFT);
ChainHead chainHead = new ChainHead(A, ConstraintWidget.VERTICAL, false);
+ chainHead.define();
assertEquals(chainHead.getHead(), A);
assertEquals(chainHead.getFirst(), A);
@@ -113,6 +117,7 @@ public class ChainHeadTest {
A.setVisibility(ConstraintWidget.GONE);
chainHead = new ChainHead(A, ConstraintWidget.VERTICAL, false);
+ chainHead.define();
assertEquals(chainHead.getHead(), A);
assertEquals(chainHead.getFirst(), A);
@@ -120,6 +125,7 @@ public class ChainHeadTest {
chainHead = new ChainHead(A, ConstraintWidget.VERTICAL, true);
+ chainHead.define();
assertEquals(chainHead.getHead(), A);
assertEquals(chainHead.getFirst(), A);
@@ -159,6 +165,7 @@ public class ChainHeadTest {
C.setHorizontalWeight(3f);
ChainHead chainHead = new ChainHead(A, ConstraintWidget.HORIZONTAL, false);
+ chainHead.define();
assertEquals(chainHead.getFirstMatchConstraintWidget(), A);
assertEquals(chainHead.getLastMatchConstraintWidget(), C);
@@ -167,6 +174,7 @@ public class ChainHeadTest {
C.setVisibility(ConstraintWidget.GONE);
chainHead = new ChainHead(A, ConstraintWidget.HORIZONTAL, false);
+ chainHead.define();
assertEquals(chainHead.getFirstMatchConstraintWidget(), A);
assertEquals(chainHead.getLastMatchConstraintWidget(), B);