summaryrefslogtreecommitdiff
path: root/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetGroup.java
diff options
context:
space:
mode:
Diffstat (limited to 'solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetGroup.java')
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetGroup.java245
1 files changed, 245 insertions, 0 deletions
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetGroup.java b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetGroup.java
new file mode 100644
index 0000000..6781dcc
--- /dev/null
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetGroup.java
@@ -0,0 +1,245 @@
+ /*
+ * Copyright (C) 2018 The Android Open Source Project * Copyright (C) 201
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.support.constraint.solver.widgets;
+
+ import static android.support.constraint.solver.widgets.ConstraintWidget.HORIZONTAL;
+ import static android.support.constraint.solver.widgets.ConstraintWidget.UNKNOWN;
+ import static android.support.constraint.solver.widgets.ConstraintWidget.VERTICAL;
+
+ import java.util.ArrayList;
+ import java.util.HashSet;
+ import java.util.List;
+ import java.util.Set;
+
+ /**
+ * Class for groups of widgets constrained between each other in a ConstraintWidgetContainer.
+ * <p>
+ * Will possess the list of ConstraintWidget in the group.
+ * The mChains that exist in the group.
+ * Each group can be solved for individually.
+ */
+ public class ConstraintWidgetGroup {
+
+ public List<ConstraintWidget> mConstrainedGroup;
+ int mGroupWidth = UNKNOWN;
+ int mGroupHeight = UNKNOWN;
+ public boolean mSkipSolver = false;
+ public final int[] mGroupDimensions = {mGroupWidth, mGroupHeight};
+ /**
+ * Arrays to contain the widgets that determine the start of a group relative to their layout.
+ * A widget is at the start, if their left/top anchor is constrained to their parent.
+ * If the left/top constraint is null, is considered at the start if there are no widgets
+ * constrained to it from their right/bottom anchor.
+ */
+ List<ConstraintWidget> mStartHorizontalWidgets = new ArrayList<>();
+ List<ConstraintWidget> mStartVerticalWidgets = new ArrayList<>();
+ HashSet<ConstraintWidget> mWidgetsToSetHorizontal = new HashSet<>();
+ HashSet<ConstraintWidget> mWidgetsToSetVertical = new HashSet<>();
+ List<ConstraintWidget> mWidgetsToSolve = new ArrayList<>();
+ List<ConstraintWidget> mUnresolvedWidgets = new ArrayList<>();
+
+ ConstraintWidgetGroup(List<ConstraintWidget> widgets) {
+ this.mConstrainedGroup = widgets;
+ }
+
+ ConstraintWidgetGroup(List<ConstraintWidget> widgets, boolean skipSolver) {
+ this.mConstrainedGroup = widgets;
+ this.mSkipSolver = skipSolver;
+ }
+
+ public List<ConstraintWidget> getStartWidgets(int orientation) {
+ if (orientation == HORIZONTAL) {
+ return mStartHorizontalWidgets;
+ } else if (orientation == VERTICAL) {
+ return mStartVerticalWidgets;
+ }
+ return null;
+ }
+
+ Set<ConstraintWidget> getWidgetsToSet(int orientation) {
+ if (orientation == HORIZONTAL) {
+ return mWidgetsToSetHorizontal;
+ } else if (orientation == VERTICAL) {
+ return mWidgetsToSetVertical;
+ }
+ return null;
+ }
+
+ void addWidgetsToSet(ConstraintWidget widget, int orientation) {
+ if (orientation == HORIZONTAL) {
+ mWidgetsToSetHorizontal.add(widget);
+ } else if (orientation == VERTICAL) {
+ mWidgetsToSetVertical.add(widget);
+ }
+ }
+
+ /**
+ * Get a list of widgets that haven't been fully resolved and require the Linear Solver
+ * to resolve.
+ * Sets {@link #mUnresolvedWidgets} with the widgets that haven't been resolved, but don't
+ * require the Linear Solver.
+ *
+ * @return List of widgets to be solved.
+ */
+ List<ConstraintWidget> getWidgetsToSolve() {
+ if (!mWidgetsToSolve.isEmpty()) {
+ return mWidgetsToSolve;
+ }
+ final int size = mConstrainedGroup.size();
+ for (int i = 0; i < size; i++) {
+ ConstraintWidget widget = mConstrainedGroup.get(i);
+ if (!widget.mOptimizerMeasurable) {
+ getWidgetsToSolveTraversal((ArrayList<ConstraintWidget>)mWidgetsToSolve, widget);
+ }
+ }
+ mUnresolvedWidgets.clear();
+ mUnresolvedWidgets.addAll(mConstrainedGroup);
+ mUnresolvedWidgets.removeAll(mWidgetsToSolve);
+ return mWidgetsToSolve;
+ }
+
+ /**
+ * Helper method to find widgets to be solved.
+ *
+ * @param widgetsToSolve Current list of widgets to be solved.
+ * @param widget Widget being traversed.
+ */
+ private void getWidgetsToSolveTraversal(ArrayList<ConstraintWidget> widgetsToSolve, ConstraintWidget widget) {
+ if (widget.mGroupsToSolver) {
+ return;
+ }
+ widgetsToSolve.add(widget);
+ widget.mGroupsToSolver = true;
+ if (widget.isFullyResolved()) {
+ return;
+ }
+ if (widget instanceof Helper) {
+ Helper helper = (Helper) widget;
+ final int widgetCount = helper.mWidgetsCount;
+ for (int i = 0; i < widgetCount; i++) {
+ getWidgetsToSolveTraversal(widgetsToSolve, helper.mWidgets[i]);
+ }
+ }
+ // Propagate from every unmeasurable widget to the parent.
+ final int count = widget.mListAnchors.length;
+ for (int i = 0; i < count; i++) {
+ ConstraintAnchor targetAnchor = widget.mListAnchors[i].mTarget;
+ ConstraintWidget targetWidget = null;
+ if (targetAnchor != null) {
+ targetWidget = targetAnchor.mOwner;
+ } else {
+ continue;
+ }
+ // Traverse until we hit a resolved widget or the parent.
+ if (targetAnchor != null && (targetWidget != widget.getParent())) {
+ getWidgetsToSolveTraversal(widgetsToSolve, targetWidget);
+ }
+ }
+ }
+
+ /**
+ * After solving, update any widgets that depended on unmeasurable widgets.
+ */
+ void updateUnresolvedWidgets() {
+ final int size = mUnresolvedWidgets.size();
+ for (int i = 0; i < size; i++) {
+ ConstraintWidget widget = mUnresolvedWidgets.get(i);
+ // Needs start, end, orientation.
+ // Or left,right/top, bottom.
+ updateResolvedDimension(widget);
+ }
+ }
+
+ /**
+ * Update widget's dimension according to the widget it depends on.
+ *
+ * @param widget Widget to resolve dimension.
+ */
+ private void updateResolvedDimension(ConstraintWidget widget) {
+ int start = 0, end = 0;
+ if (widget.mOptimizerMeasurable) {
+ // No need to update dimension if it has been resolved.
+ if (widget.isFullyResolved()) {
+ return;
+ }
+ // Horizontal.
+ boolean rightSide = widget.mRight.mTarget != null;
+ ConstraintAnchor targetAnchor;
+ // Get measure if target is resolved, otherwise, resolve target.
+ if (rightSide) {
+ targetAnchor = widget.mRight.mTarget;
+ } else {
+ targetAnchor = widget.mLeft.mTarget;
+ }
+ if (targetAnchor != null) {
+ if (!targetAnchor.mOwner.mOptimizerMeasured) {
+ updateResolvedDimension(targetAnchor.mOwner);
+ }
+ if (targetAnchor.mType == ConstraintAnchor.Type.RIGHT) {
+ end = targetAnchor.mOwner.mX + targetAnchor.mOwner.getWidth();
+ } else if (targetAnchor.mType == ConstraintAnchor.Type.LEFT) {
+ end = targetAnchor.mOwner.mX;
+ }
+ }
+ if (rightSide) {
+ end -= widget.mRight.getMargin();
+ } else {
+ end += widget.mLeft.getMargin() + widget.getWidth();
+ }
+ start = end - widget.getWidth();
+ widget.setHorizontalDimension(start, end);
+ // Vertical.
+ if (widget.mBaseline.mTarget != null) {
+ targetAnchor = widget.mBaseline.mTarget;
+ if (!targetAnchor.mOwner.mOptimizerMeasured) {
+ updateResolvedDimension(targetAnchor.mOwner);
+ }
+ start = targetAnchor.mOwner.mY + targetAnchor.mOwner.mBaselineDistance
+ - widget.mBaselineDistance;
+ end = start + widget.mHeight;
+ widget.setVerticalDimension(start, end);
+ widget.mOptimizerMeasured = true;
+ return;
+ }
+ boolean bottomSide = widget.mBottom.mTarget != null;
+ // Get measure if target is resolved, otherwise, resolve target.
+ if (bottomSide) {
+ targetAnchor = widget.mBottom.mTarget;
+ } else {
+ targetAnchor = widget.mTop.mTarget;
+ }
+ if (targetAnchor != null) {
+ if (!targetAnchor.mOwner.mOptimizerMeasured) {
+ updateResolvedDimension(targetAnchor.mOwner);
+ }
+ if (targetAnchor.mType == ConstraintAnchor.Type.BOTTOM) {
+ end = targetAnchor.mOwner.mY + targetAnchor.mOwner.getHeight();
+ } else if (targetAnchor.mType == ConstraintAnchor.Type.TOP) {
+ end = targetAnchor.mOwner.mY;
+ }
+ }
+ if (bottomSide) {
+ end -= widget.mBottom.getMargin();
+ } else {
+ end += widget.mTop.getMargin() + widget.getHeight();
+ }
+ start = end - widget.getHeight();
+ widget.setVerticalDimension(start, end);
+ widget.mOptimizerMeasured = true;
+ }
+ }
+ }