RunGroup.java
/*
* Copyright (C) 2019 The Android Open Source Project
*
* 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 androidx.constraintlayout.core.widgets.analyzer;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.HORIZONTAL;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL;
import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
import java.util.ArrayList;
class RunGroup {
public static final int START = 0;
public static final int END = 1;
public static final int BASELINE = 2;
public static int index;
public int position = 0;
public boolean dual = false;
WidgetRun mFirstRun = null;
WidgetRun mLastRun = null;
ArrayList<WidgetRun> mRuns = new ArrayList<>();
int mGroupIndex = 0;
int mDirection;
RunGroup(WidgetRun run, int dir) {
mGroupIndex = index;
index++;
mFirstRun = run;
mLastRun = run;
mDirection = dir;
}
public void add(WidgetRun run) {
mRuns.add(run);
mLastRun = run;
}
private long traverseStart(DependencyNode node, long startPosition) {
WidgetRun run = node.mRun;
if (run instanceof HelperReferences) {
return startPosition;
}
long position = startPosition;
// first, compute stuff dependent on this node.
final int count = node.mDependencies.size();
for (int i = 0; i < count; i++) {
Dependency dependency = node.mDependencies.get(i);
if (dependency instanceof DependencyNode) {
DependencyNode nextNode = (DependencyNode) dependency;
if (nextNode.mRun == run) {
// skip our own sibling node
continue;
}
position = Math.max(position,
traverseStart(nextNode, startPosition + nextNode.mMargin));
}
}
if (node == run.start) {
// let's go for our sibling
long dimension = run.getWrapDimension();
position = Math.max(position, traverseStart(run.end, startPosition + dimension));
position = Math.max(position, startPosition + dimension - run.end.mMargin);
}
return position;
}
private long traverseEnd(DependencyNode node, long startPosition) {
WidgetRun run = node.mRun;
if (run instanceof HelperReferences) {
return startPosition;
}
long position = startPosition;
// first, compute stuff dependent on this node.
final int count = node.mDependencies.size();
for (int i = 0; i < count; i++) {
Dependency dependency = node.mDependencies.get(i);
if (dependency instanceof DependencyNode) {
DependencyNode nextNode = (DependencyNode) dependency;
if (nextNode.mRun == run) {
// skip our own sibling node
continue;
}
position = Math.min(position,
traverseEnd(nextNode, startPosition + nextNode.mMargin));
}
}
if (node == run.end) {
// let's go for our sibling
long dimension = run.getWrapDimension();
position = Math.min(position, traverseEnd(run.start, startPosition - dimension));
position = Math.min(position, startPosition - dimension - run.start.mMargin);
}
return position;
}
public long computeWrapSize(ConstraintWidgetContainer container, int orientation) {
if (mFirstRun instanceof ChainRun) {
ChainRun chainRun = (ChainRun) mFirstRun;
if (chainRun.orientation != orientation) {
return 0;
}
} else {
if (orientation == HORIZONTAL) {
if (!(mFirstRun instanceof HorizontalWidgetRun)) {
return 0;
}
} else {
if (!(mFirstRun instanceof VerticalWidgetRun)) {
return 0;
}
}
}
DependencyNode containerStart = orientation == HORIZONTAL
? container.mHorizontalRun.start : container.mVerticalRun.start;
DependencyNode containerEnd = orientation == HORIZONTAL
? container.mHorizontalRun.end : container.mVerticalRun.end;
boolean runWithStartTarget = mFirstRun.start.mTargets.contains(containerStart);
boolean runWithEndTarget = mFirstRun.end.mTargets.contains(containerEnd);
long dimension = mFirstRun.getWrapDimension();
if (runWithStartTarget && runWithEndTarget) {
long maxPosition = traverseStart(mFirstRun.start, 0);
long minPosition = traverseEnd(mFirstRun.end, 0);
// to compute the gaps, we subtract the margins
long endGap = maxPosition - dimension;
if (endGap >= -mFirstRun.end.mMargin) {
endGap += mFirstRun.end.mMargin;
}
long startGap = -minPosition - dimension - mFirstRun.start.mMargin;
if (startGap >= mFirstRun.start.mMargin) {
startGap -= mFirstRun.start.mMargin;
}
float bias = mFirstRun.mWidget.getBiasPercent(orientation);
long gap = 0;
if (bias > 0) {
gap = (long) ((startGap / bias) + (endGap / (1f - bias)));
}
startGap = (long) (0.5f + (gap * bias));
endGap = (long) (0.5f + (gap * (1f - bias)));
long runDimension = startGap + dimension + endGap;
dimension = mFirstRun.start.mMargin + runDimension - mFirstRun.end.mMargin;
} else if (runWithStartTarget) {
long maxPosition = traverseStart(mFirstRun.start, mFirstRun.start.mMargin);
long runDimension = mFirstRun.start.mMargin + dimension;
dimension = Math.max(maxPosition, runDimension);
} else if (runWithEndTarget) {
long minPosition = traverseEnd(mFirstRun.end, mFirstRun.end.mMargin);
long runDimension = -mFirstRun.end.mMargin + dimension;
dimension = Math.max(-minPosition, runDimension);
} else {
dimension = mFirstRun.start.mMargin
+ mFirstRun.getWrapDimension() - mFirstRun.end.mMargin;
}
return dimension;
}
private boolean defineTerminalWidget(WidgetRun run, int orientation) {
if (!run.mWidget.isTerminalWidget[orientation]) {
return false;
}
for (Dependency dependency : run.start.mDependencies) {
if (dependency instanceof DependencyNode) {
DependencyNode node = (DependencyNode) dependency;
if (node.mRun == run) {
continue;
}
if (node == node.mRun.start) {
if (run instanceof ChainRun) {
ChainRun chainRun = (ChainRun) run;
for (WidgetRun widgetChainRun : chainRun.mWidgets) {
defineTerminalWidget(widgetChainRun, orientation);
}
} else {
if (!(run instanceof HelperReferences)) {
run.mWidget.isTerminalWidget[orientation] = false;
}
}
defineTerminalWidget(node.mRun, orientation);
}
}
}
for (Dependency dependency : run.end.mDependencies) {
if (dependency instanceof DependencyNode) {
DependencyNode node = (DependencyNode) dependency;
if (node.mRun == run) {
continue;
}
if (node == node.mRun.start) {
if (run instanceof ChainRun) {
ChainRun chainRun = (ChainRun) run;
for (WidgetRun widgetChainRun : chainRun.mWidgets) {
defineTerminalWidget(widgetChainRun, orientation);
}
} else {
if (!(run instanceof HelperReferences)) {
run.mWidget.isTerminalWidget[orientation] = false;
}
}
defineTerminalWidget(node.mRun, orientation);
}
}
}
return false;
}
public void defineTerminalWidgets(boolean horizontalCheck, boolean verticalCheck) {
if (horizontalCheck && mFirstRun instanceof HorizontalWidgetRun) {
defineTerminalWidget(mFirstRun, HORIZONTAL);
}
if (verticalCheck && mFirstRun instanceof VerticalWidgetRun) {
defineTerminalWidget(mFirstRun, VERTICAL);
}
}
}