/*
* Copyright (C) 2020 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 androidx.constraintlayout.core.LinearSystem;
import androidx.constraintlayout.core.widgets.Chain;
import androidx.constraintlayout.core.widgets.ConstraintWidget;
import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.BOTH;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.HORIZONTAL;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL;
/**
* Represents a group of widget for the grouping mechanism.
*/
public class WidgetGroup {
private static final boolean DEBUG = false;
ArrayList<ConstraintWidget> widgets = new ArrayList<>();
static int count = 0;
int id = -1;
boolean authoritative = false;
int orientation = HORIZONTAL;
ArrayList<MeasureResult> results = null;
private int moveTo = -1;
public WidgetGroup(int orientation) {
id = count++;
this.orientation = orientation;
}
public int getOrientation() { return orientation; }
public int getId() { return id; }
public boolean add(ConstraintWidget widget) {
if (widgets.contains(widget)) {
return false;
}
widgets.add(widget);
return true;
}
public void setAuthoritative(boolean isAuthoritative) { authoritative = isAuthoritative; }
public boolean isAuthoritative() { return authoritative; }
private String getOrientationString() {
if (orientation == HORIZONTAL) {
return "Horizontal";
} else if (orientation == VERTICAL) {
return "Vertical";
} else if (orientation == BOTH) {
return "Both";
}
return "Unknown";
}
@Override
public String toString() {
String ret = getOrientationString() + " [" + id + "] <";
for (ConstraintWidget widget : widgets) {
ret += " " + widget.getDebugName();
}
ret += " >";
return ret;
}
public void moveTo(int orientation, WidgetGroup widgetGroup) {
if (DEBUG) {
System.out.println("Move all widgets (" + this + ") from " + id + " to " + widgetGroup.getId() + "(" + widgetGroup + ")");
}
for (ConstraintWidget widget : widgets) {
widgetGroup.add(widget);
if (orientation == HORIZONTAL) {
widget.horizontalGroup = widgetGroup.getId();
} else {
widget.verticalGroup = widgetGroup.getId();
}
}
moveTo = widgetGroup.id;
}
public void clear() {
widgets.clear();
}
private int measureWrap(int orientation, ConstraintWidget widget) {
ConstraintWidget.DimensionBehaviour behaviour = widget.getDimensionBehaviour(orientation);
if (behaviour == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT
|| behaviour == ConstraintWidget.DimensionBehaviour.MATCH_PARENT
|| behaviour == ConstraintWidget.DimensionBehaviour.FIXED) {
int dimension;
if (orientation == HORIZONTAL) {
dimension = widget.getWidth();
} else {
dimension = widget.getHeight();
}
return dimension;
}
return -1;
}
public int measureWrap(LinearSystem system, int orientation) {
int count = widgets.size();
if (count == 0) {
return 0;
}
// TODO: add direct wrap computation for simpler cases instead of calling the solver
return solverMeasure(system, widgets, orientation);
}
private int solverMeasure(LinearSystem system, ArrayList<ConstraintWidget> widgets, int orientation) {
ConstraintWidgetContainer container = (ConstraintWidgetContainer) widgets.get(0).getParent();
system.reset();
boolean prevDebug = LinearSystem.FULL_DEBUG;
container.addToSolver(system, false);
for (int i = 0; i < widgets.size(); i++) {
ConstraintWidget widget = widgets.get(i);
widget.addToSolver(system, false);
}
if (orientation == HORIZONTAL) {
if (container.mHorizontalChainsSize > 0) {
Chain.applyChainConstraints(container, system, widgets, HORIZONTAL);
}
}
if (orientation == VERTICAL) {
if (container.mVerticalChainsSize > 0) {
Chain.applyChainConstraints(container, system, widgets, VERTICAL);
}
}
try {
system.minimize();
} catch (Exception e) {
e.printStackTrace();
}
// save results
results = new ArrayList<>();
for (int i = 0; i < widgets.size(); i++) {
ConstraintWidget widget = widgets.get(i);
MeasureResult result = new MeasureResult(widget, system, orientation);
results.add(result);
}
if (orientation == HORIZONTAL) {
int left = system.getObjectVariableValue(container.mLeft);
int right = system.getObjectVariableValue(container.mRight);
system.reset();
return right - left;
} else {
int top = system.getObjectVariableValue(container.mTop);
int bottom = system.getObjectVariableValue(container.mBottom);
system.reset();
return bottom - top;
}
}
public void setOrientation(int orientation) {
this.orientation = orientation;
}
public void apply() {
if (results == null) {
return;
}
if (!authoritative) {
return;
}
for (int i = 0; i < results.size(); i++) {
MeasureResult result = results.get(i);
result.apply();
}
}
public boolean intersectWith(WidgetGroup group) {
for (int i = 0; i < widgets.size(); i++) {
ConstraintWidget widget = widgets.get(i);
if (group.contains(widget)) {
return true;
}
}
return false;
}
private boolean contains(ConstraintWidget widget) {
return widgets.contains(widget);
}
public int size() {
return widgets.size();
}
public void cleanup(ArrayList<WidgetGroup> dependencyLists) {
final int count = widgets.size();
if (moveTo != -1 && count > 0) {
for (int i = 0; i < dependencyLists.size(); i++) {
WidgetGroup group = dependencyLists.get(i);
if (moveTo == group.id) {
moveTo(orientation, group);
}
}
}
if (count == 0) {
dependencyLists.remove(this);
return;
}
}
class MeasureResult {
WeakReference<ConstraintWidget> widgetRef;
int left;
int top;
int right;
int bottom;
int baseline;
int orientation;
public MeasureResult(ConstraintWidget widget, LinearSystem system, int orientation) {
widgetRef = new WeakReference<>(widget);
left = system.getObjectVariableValue(widget.mLeft);
top = system.getObjectVariableValue(widget.mTop);
right = system.getObjectVariableValue(widget.mRight);
bottom = system.getObjectVariableValue(widget.mBottom);
baseline = system.getObjectVariableValue(widget.mBaseline);
this.orientation = orientation;
}
public void apply() {
ConstraintWidget widget = widgetRef.get();
if (widget != null) {
widget.setFinalFrame(left, top, right, bottom, baseline, orientation);
}
}
}
}