From 8589ebbaf4d9409084407f7d51a6f5765c8c2d04 Mon Sep 17 00:00:00 2001 From: Nicolas Roard Date: Tue, 12 Jun 2018 03:38:12 -0700 Subject: 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 --- .../support/constraint/ConstraintLayout.java | 10 +- .../support/constraint/solver/ArrayRow.java | 34 +++--- .../support/constraint/solver/LinearSystem.java | 7 ++ .../support/constraint/solver/widgets/Chain.java | 86 ++++++++------ .../constraint/solver/widgets/ChainHead.java | 62 +++++++++- .../solver/widgets/ConstraintWidget.java | 38 +++++- .../solver/widgets/ConstraintWidgetContainer.java | 6 + .../constraint/solver/AdvancedChainTest.java | 128 +++++++++++++++++++++ .../support/constraint/solver/ChainHeadTest.java | 8 ++ 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 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 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; @@ -675,6 +677,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 * @@ -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 @@ -29,6 +29,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); 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); -- cgit v1.2.3