in designer/src/com/android/tools/idea/uibuilder/scout/ScoutArrange.java [65:683]
public static void align(Scout.Arrange type, List<NlComponent> widgetList,
boolean applyConstraints) {
if (widgetList == null || widgetList.isEmpty()) {
return;
}
if (widgetList.get(0).getParent() == null) {
return;
}
ScoutWidget parentScoutWidget = new ScoutWidget(widgetList.get(0).getParent(), null);
ScoutWidget[] scoutWidgets = ScoutWidget.create(widgetList, parentScoutWidget);
int margin = Scout.getMargin();
switch (type) {
case AlignHorizontallyCenter:
case AlignHorizontallyLeft:
case AlignHorizontallyRight:
Arrays.sort(scoutWidgets, sSortRecY);
if (rootDistance(scoutWidgets[0].mNlComponent) > rootDistance(scoutWidgets[scoutWidgets.length - 1].mNlComponent)) {
reverse(scoutWidgets);
}
break;
case DistributeVertically:
Arrays.sort(scoutWidgets, sSortRecY);
break;
case AlignVerticallyTop:
case AlignVerticallyMiddle:
case AlignBaseline:
case AlignVerticallyBottom:
Arrays.sort(scoutWidgets, sSortRecX);
if (rootDistance(scoutWidgets[0].mNlComponent) > rootDistance(scoutWidgets[scoutWidgets.length - 1].mNlComponent)) {
reverse(scoutWidgets);
}
break;
case DistributeHorizontally:
Arrays.sort(scoutWidgets, sSortRecX);
break;
default:
}
switch (type) {
case ConnectTop:
connect(scoutWidgets, parentScoutWidget, Direction.TOP, (applyConstraints) ? 0 : -1);
break;
case ConnectBottom:
connect(scoutWidgets, parentScoutWidget, Direction.BOTTOM, (applyConstraints) ? 0 : -1);
break;
case ConnectStart:
connect(scoutWidgets, parentScoutWidget, Direction.LEFT, (applyConstraints) ? 0 : -1);
break;
case ConnectEnd:
connect(scoutWidgets, parentScoutWidget, Direction.RIGHT, (applyConstraints) ? 0 : -1);
break;
case CreateHorizontalChain: {
Arrays.sort(scoutWidgets, sSortRecX);
Rectangle rectangle = new Rectangle();
NlComponent parent = parentScoutWidget.mNlComponent;
ScoutWidget[] peers = ScoutWidget.create(parent.getChildren(), parentScoutWidget);
ScoutWidget leftConnect = null;
for (int i = 0; i < scoutWidgets.length; i++) {
ScoutWidget widget = scoutWidgets[i];
ScoutWidget rightConnect;
if (i + 1 < scoutWidgets.length) {
rightConnect = scoutWidgets[i + 1];
}
else {
rectangle.x = widget.getDpX();
rectangle.y = widget.getDpY();
rectangle.width = widget.getDpWidth();
rectangle.height = widget.getDpHeight();
rightConnect = gapWidget(Direction.RIGHT, rectangle, peers, parentScoutWidget);
}
if (leftConnect == null) {
rectangle.x = widget.getDpX();
rectangle.y = widget.getDpY();
rectangle.width = widget.getDpWidth();
rectangle.height = widget.getDpHeight();
leftConnect = gapWidget(Direction.LEFT, rectangle, peers, parentScoutWidget);
}
else {
leftConnect = scoutWidgets[i - 1];
}
Direction dir = Direction.RIGHT;
if (leftConnect == parentScoutWidget) {
dir = Direction.LEFT;
}
scoutConnect(widget.mNlComponent, Direction.LEFT, leftConnect.mNlComponent, dir, 0);
dir = Direction.LEFT;
if (rightConnect == parentScoutWidget) {
dir = Direction.RIGHT;
}
scoutConnect(widget.mNlComponent, Direction.RIGHT, rightConnect.mNlComponent, dir, 0);
setScoutHorizontalBiasPercent(widget.mNlComponent, .5f);
}
}
break;
case CreateVerticalChain: {
Arrays.sort(scoutWidgets, sSortRecY);
Rectangle rectangle = new Rectangle();
NlComponent parent = parentScoutWidget.mNlComponent;
ScoutWidget[] peers = ScoutWidget.create(parent.getChildren(), parentScoutWidget);
ScoutWidget topConnect = null;
for (int i = 0; i < scoutWidgets.length; i++) {
ScoutWidget widget = scoutWidgets[i];
ScoutWidget bottomConnect;
if (i + 1 < scoutWidgets.length) {
bottomConnect = scoutWidgets[i + 1];
}
else {
rectangle.x = widget.getDpX();
rectangle.y = widget.getDpY();
rectangle.width = widget.getDpWidth();
rectangle.height = widget.getDpHeight();
bottomConnect = gapWidget(Direction.BOTTOM, rectangle, peers, parentScoutWidget);
}
if (topConnect == null) {
rectangle.x = widget.getDpX();
rectangle.y = widget.getDpY();
rectangle.width = widget.getDpWidth();
rectangle.height = widget.getDpHeight();
topConnect = gapWidget(Direction.TOP, rectangle, peers, parentScoutWidget);
}
else {
topConnect = scoutWidgets[i - 1];
}
Direction dir = Direction.BOTTOM;
if (topConnect == parentScoutWidget) {
dir = Direction.TOP;
}
scoutConnect(widget.mNlComponent, Direction.TOP, topConnect.mNlComponent, dir, 0);
dir = Direction.TOP;
if (bottomConnect == parentScoutWidget) {
dir = Direction.BOTTOM;
}
scoutConnect(widget.mNlComponent, Direction.BOTTOM, bottomConnect.mNlComponent, dir, 0);
setScoutHorizontalBiasPercent(widget.mNlComponent, .5f);
}
}
break;
case CenterHorizontally: {
Rectangle rectangle = new Rectangle();
NlComponent parent = parentScoutWidget.mNlComponent;
ScoutWidget[] peers = ScoutWidget.create(parent.getChildren(), parentScoutWidget);
for (ScoutWidget widget : scoutWidgets) {
rectangle.x = widget.getDpX();
rectangle.y = widget.getDpY();
rectangle.width = widget.getDpWidth();
rectangle.height = widget.getDpHeight();
int westDistance = gap(Direction.LEFT, rectangle, peers, parentScoutWidget);
int eastDistance = gap(Direction.RIGHT, rectangle, peers, parentScoutWidget);
int x = widget.getDpX();
if (applyConstraints) {
ScoutWidget westConnect =
gapWidget(Direction.LEFT, rectangle, peers, parentScoutWidget);
ScoutWidget eastConnect =
gapWidget(Direction.RIGHT, rectangle, peers, parentScoutWidget);
Direction dir = Direction.RIGHT;
if (westConnect == parentScoutWidget) {
dir = Direction.LEFT;
}
scoutConnect(widget.mNlComponent, Direction.LEFT, westConnect.mNlComponent, dir, 0);
dir = Direction.LEFT;
if (eastConnect == parentScoutWidget) {
dir = Direction.RIGHT;
}
scoutConnect(widget.mNlComponent, Direction.RIGHT, eastConnect.mNlComponent, dir, 0);
setScoutHorizontalBiasPercent(widget.mNlComponent, .5f);
}
else {
setScoutAbsoluteDpX(widget.mNlComponent, x + (eastDistance - westDistance) / 2, true);
}
}
}
break;
case CenterVertically: {
Rectangle rectangle = new Rectangle();
NlComponent parent = parentScoutWidget.mNlComponent;
ScoutWidget[] peers = ScoutWidget.create(parent.getChildren(), parentScoutWidget);
for (ScoutWidget widget : scoutWidgets) {
rectangle.x = widget.getDpX();
rectangle.y = widget.getDpY();
rectangle.width = widget.getDpWidth();
rectangle.height = widget.getDpHeight();
int northDistance = gap(Direction.TOP, rectangle, peers, parentScoutWidget);
int southDistance = gap(Direction.BOTTOM, rectangle, peers, parentScoutWidget);
int Y = widget.getDpY();
if (applyConstraints) {
ScoutWidget northConnect =
gapWidget(Direction.TOP, rectangle, peers, parentScoutWidget);
ScoutWidget southConnect =
gapWidget(Direction.BOTTOM, rectangle, peers, parentScoutWidget);
Direction dir = Direction.BOTTOM;
if (northConnect == parentScoutWidget) {
dir = Direction.TOP;
}
scoutConnect(widget.mNlComponent, Direction.TOP, northConnect.mNlComponent, dir, 0);
dir = Direction.TOP;
if (southConnect == parentScoutWidget) {
dir = Direction.BOTTOM;
}
scoutConnect(widget.mNlComponent, Direction.BOTTOM, southConnect.mNlComponent, dir, 0);
setScoutVerticalBiasPercent(widget.mNlComponent, .5f);
}
else {
setScoutAbsoluteDpY(widget.mNlComponent, Y + (southDistance - northDistance) / 2, true);
}
}
}
break;
case CenterHorizontallyInParent: {
for (ScoutWidget widget : scoutWidgets) {
int parentWidth = parentScoutWidget.getDpWidth();
int width = widget.getDpWidth();
setScoutAbsoluteDpX(widget.mNlComponent, (parentWidth - width) / 2, true);
if (applyConstraints) {
scoutConnect(widget.mNlComponent, Direction.LEFT, parentScoutWidget.mNlComponent, Direction.LEFT, 0);
scoutConnect(widget.mNlComponent, Direction.RIGHT, parentScoutWidget.mNlComponent, Direction.RIGHT, 0);
setScoutHorizontalBiasPercent(widget.mNlComponent, .5f);
}
}
}
break;
case CenterVerticallyInParent: {
for (ScoutWidget widget : scoutWidgets) {
int parentHeight = parentScoutWidget.getDpHeight();
int height = widget.getDpHeight();
setScoutAbsoluteDpY(widget.mNlComponent, (parentHeight - height) / 2, true);
if (applyConstraints) {
scoutConnect(widget.mNlComponent, Direction.TOP, parentScoutWidget.mNlComponent, Direction.TOP, 0);
scoutConnect(widget.mNlComponent, Direction.BOTTOM, parentScoutWidget.mNlComponent, Direction.BOTTOM, 0);
setScoutVerticalBiasPercent(widget.mNlComponent, .5f);
}
}
}
break;
case AlignHorizontallyCenter: {
int count = 0;
float avg = 0;
for (ScoutWidget widget : scoutWidgets) {
avg += widget.getDpX() + widget.getDpWidth() / 2.0f;
count++;
}
avg /= count;
NlComponent previousWidget = null;
for (ScoutWidget widget : scoutWidgets) {
float current = widget.getDpWidth() / 2.0f;
setScoutAbsoluteDpX(widget.mNlComponent, (int)(avg - current), true);
if (applyConstraints) {
if (previousWidget != null) {
scoutConnect(widget.mNlComponent, Direction.LEFT, previousWidget, Direction.LEFT, 0);
scoutConnect(widget.mNlComponent, Direction.RIGHT, previousWidget, Direction.RIGHT, 0);
}
}
previousWidget = widget.mNlComponent;
}
}
break;
case AlignHorizontallyLeft: {
int min = Integer.MAX_VALUE;
if (applyConstraints) { // test if already connected to flip directions
flipConnectionsAndReverse(scoutWidgets, Direction.LEFT, ourLeftAttributes, ourStartAttributes);
}
for (ScoutWidget widget : scoutWidgets) {
min = Math.min(min, widget.getDpX());
}
NlComponent previousWidget = null;
for (ScoutWidget widget : scoutWidgets) {
setScoutAbsoluteDpX(widget.mNlComponent, min, true);
if (applyConstraints) {
if (previousWidget != null) {
scoutClearAttributes(widget.mNlComponent, ourRightAttributes);
scoutClearAttributes(widget.mNlComponent, ourEndAttributes);
scoutConnect(widget.mNlComponent, Direction.LEFT, previousWidget,
Direction.LEFT, 0);
}
}
previousWidget = widget.mNlComponent;
}
}
break;
case AlignHorizontallyRight: {
int max = Integer.MIN_VALUE;
if (applyConstraints) { // test if already connected to flip directions
flipConnectionsAndReverse(scoutWidgets, Direction.RIGHT, ourRightAttributes, ourEndAttributes);
}
for (ScoutWidget widget : scoutWidgets) {
max = Math.max(max, widget.getDpX() + widget.getDpWidth());
}
NlComponent previousWidget = null;
for (ScoutWidget widget : scoutWidgets) {
float current = widget.getDpWidth();
setScoutAbsoluteDpX(widget.mNlComponent, (int)(max - current), true);
if (applyConstraints) {
if (previousWidget != null) {
scoutClearAttributes(widget.mNlComponent, ourLeftAttributes);
scoutClearAttributes(widget.mNlComponent, ourStartAttributes);
scoutConnect(widget.mNlComponent, Direction.RIGHT, previousWidget,
Direction.RIGHT, 0);
}
}
previousWidget = widget.mNlComponent;
}
}
break;
case AlignVerticallyTop: {
int min = Integer.MAX_VALUE;
if (applyConstraints) { // test if already connected to flip directions
flipConnectionsAndReverse(scoutWidgets, Direction.TOP, ourTopAttributes, null);
}
for (ScoutWidget widget : scoutWidgets) {
min = Math.min(min, widget.getDpY());
}
NlComponent previousWidget = null;
for (ScoutWidget widget : scoutWidgets) {
setScoutAbsoluteDpY(widget.mNlComponent, min, true);
if (applyConstraints) {
if (previousWidget != null) {
scoutClearAttributes(widget.mNlComponent, ourBottomAttributes);
scoutConnect(widget.mNlComponent, Direction.TOP, previousWidget,
Direction.TOP, 0);
}
}
previousWidget = widget.mNlComponent;
}
}
break;
case AlignVerticallyMiddle: {
int count = 0;
float avg = 0;
for (ScoutWidget widget : scoutWidgets) {
avg += widget.getDpY() + widget.getDpHeight() / 2.0f;
count++;
}
avg /= count;
NlComponent previousWidget = null;
for (ScoutWidget widget : scoutWidgets) {
float current = widget.getDpHeight() / 2.0f;
setScoutAbsoluteDpY(widget.mNlComponent, (int)(avg - current), true);
if (applyConstraints) {
if (previousWidget != null) {
scoutConnect(widget.mNlComponent, Direction.TOP, previousWidget, Direction.TOP, 0);
scoutConnect(widget.mNlComponent, Direction.BOTTOM, previousWidget, Direction.BOTTOM, 0);
}
}
previousWidget = widget.mNlComponent;
}
}
break;
case AlignBaseline: {
int count = 0;
float avg = 0;
if (applyConstraints) { // test if already connected to flip directions
flipBaselineAndReverse(scoutWidgets);
}
int number_of_constrained = 0;
NlComponent fixedWidget = null;
for (ScoutWidget widget : scoutWidgets) {
if (isVerticallyConstrained(widget.mNlComponent)) {
number_of_constrained++;
fixedWidget = widget.mNlComponent;
}
avg += widget.getDpY() + widget.getDpBaseline();
count++;
}
avg /= count;
// if one is already constrained move the rest to it
if (number_of_constrained == 1) {
avg = getDpX(fixedWidget) + getDpBaseline(fixedWidget);
}
NlComponent previousWidget = null;
if (!applyConstraints || number_of_constrained == 0) {
for (ScoutWidget widget : scoutWidgets) {
float baseline = widget.getDpBaseline();
setScoutAbsoluteDpY(widget.mNlComponent, (int)(avg - baseline), true);
if (applyConstraints) {
if (previousWidget != null) {
scoutConnect(widget.mNlComponent, Direction.BASELINE, previousWidget,
Direction.BASELINE, 0);
}
}
previousWidget = widget.mNlComponent;
}
}
else { // if you are creating constraints and some are already constrained
// Build a list of constrained and unconstrained widgets
ArrayList<ScoutWidget> unconstrained = new ArrayList<>();
ArrayList<ScoutWidget> constrained = new ArrayList<>();
for (ScoutWidget widget : scoutWidgets) {
if (isVerticallyConstrained(widget.mNlComponent)) {
constrained.add(widget);
}
else {
unconstrained.add(widget);
}
}
// one by one constrain widgets by finding the closest between the two list
while (!unconstrained.isEmpty()) {
ScoutWidget to = null;
ScoutWidget from = null;
int min = Integer.MAX_VALUE;
for (ScoutWidget fromCandidate : unconstrained) {
for (ScoutWidget toCandidate : constrained) {
int fromLeft = fromCandidate.getDpX();
int fromRight = fromLeft + fromCandidate.getDpWidth();
int toLeft = toCandidate.getDpX();
int toRight = toLeft + toCandidate.getDpWidth();
int dist = Math.abs(toLeft - fromLeft);
dist = Math.min(dist, Math.abs(toLeft - fromRight));
dist = Math.min(dist, Math.abs(toRight - fromRight));
dist = Math.min(dist, Math.abs(toRight - fromLeft));
if (dist < min) {
min = dist;
to = toCandidate;
from = fromCandidate;
}
}
}
scoutConnect(from.mNlComponent, Direction.BASELINE, to.mNlComponent,
Direction.BASELINE, 0);
constrained.add(from);
unconstrained.remove(from);
}
}
}
break;
case AlignVerticallyBottom: {
int max = Integer.MIN_VALUE;
if (applyConstraints) { // test if already connected to flip directions
flipConnectionsAndReverse(scoutWidgets, Direction.BOTTOM, ourBottomAttributes, null);
}
for (ScoutWidget widget : scoutWidgets) {
max = Math.max(max, widget.getDpY() + widget.getDpHeight());
}
NlComponent previousWidget = null;
for (ScoutWidget widget : scoutWidgets) {
float current = widget.getDpHeight();
setScoutAbsoluteDpY(widget.mNlComponent, (int)(max - current), true);
if (applyConstraints) {
if (previousWidget != null) {
scoutClearAttributes(widget.mNlComponent, ourTopAttributes);
scoutConnect(widget.mNlComponent, Direction.BOTTOM, previousWidget,
Direction.BOTTOM, 0);
}
}
previousWidget = widget.mNlComponent;
}
}
break;
case DistributeVertically: {
int count = 0;
int sum = 0;
int min = getDpY(widgetList.get(0));
int max = getDpY(widgetList.get(0)) + getDpHeight(widgetList.get(0));
for (ScoutWidget widget : scoutWidgets) {
int start = widget.getDpY();
int size = widget.getDpHeight();
int end = start + size;
sum += size;
min = Math.min(min, start);
max = Math.max(max, end);
count++;
}
int gaps = count - 1;
int totalGap = max - min - sum;
int lastY = min;
boolean reverse =
rootDistanceY(scoutWidgets[0].mNlComponent) > rootDistanceY(scoutWidgets[scoutWidgets.length - 1].mNlComponent);
for (int i = 0; i < count; i++) {
if (i > 0) {
int size = scoutWidgets[i - 1].getDpHeight();
min += size;
int pos = min + (totalGap * i) / gaps;
setScoutAbsoluteDpY(scoutWidgets[i].mNlComponent, pos, true);
if (applyConstraints) {
if (reverse) {
scoutConnect(scoutWidgets[i - 1].mNlComponent, Direction.BOTTOM, scoutWidgets[i].mNlComponent,
Direction.TOP, pos - lastY - size);
}
else {
scoutConnect(scoutWidgets[i].mNlComponent, Direction.TOP, scoutWidgets[i - 1].mNlComponent,
Direction.BOTTOM, pos - lastY - size);
}
lastY = pos;
}
}
}
}
break;
case DistributeHorizontally: {
int count = 0;
int sum = 0;
int min = getDpX(widgetList.get(0));
int max = getDpX(widgetList.get(0)) + getDpHeight(widgetList.get(0));
for (ScoutWidget widget : scoutWidgets) {
int start = widget.getDpX();
int size = widget.getDpWidth();
int end = start + size;
sum += size;
min = Math.min(min, start);
max = Math.max(max, end);
count++;
}
int gaps = count - 1;
int totalGap = max - min - sum;
int lastX = min;
boolean reverse =
rootDistanceX(scoutWidgets[0].mNlComponent) > rootDistanceX(scoutWidgets[scoutWidgets.length - 1].mNlComponent);
for (int i = 0; i < count; i++) {
if (i > 0) {
int size = scoutWidgets[i - 1].getDpWidth();
min += size;
int pos = min + (totalGap * i) / gaps;
setScoutAbsoluteDpX(scoutWidgets[i].mNlComponent, pos, true);
if (applyConstraints) {
if (reverse) {
scoutConnect(scoutWidgets[i - 1].mNlComponent, Direction.RIGHT, scoutWidgets[i].mNlComponent,
Direction.LEFT, pos - lastX - size);
}
else {
scoutConnect(scoutWidgets[i].mNlComponent, Direction.LEFT, scoutWidgets[i - 1].mNlComponent,
Direction.RIGHT, pos - lastX - size);
}
lastX = pos;
}
}
}
}
break;
case VerticalPack: {
NlComponent[] wArray = new NlComponent[widgetList.size()];
wArray = widgetList.toArray(wArray);
Arrays.sort(wArray, (w1, w2) -> Integer.compare(getDpY(w1), getDpY(w2)));
ScoutWidget[] list = ScoutWidget.getWidgetArray(widgetList.get(0).getParent());
Rectangle bounds = null;
for (int i = 0; i < wArray.length; i++) {
String id = SdkConstants.NEW_ID_PREFIX + wArray[i].getId();
ScoutWidget w = list[0].getChild(id);
if (bounds == null) {
bounds = new Rectangle(w.getRectangle());
}
else {
bounds = bounds.union(w.getRectangle());
}
}
for (NlComponent cw : wArray) {
for (ScoutWidget scoutWidget : list) {
if (scoutWidget.mNlComponent == cw) {
int gapN = scoutWidget.gap(Direction.TOP, list);
int newY = margin + scoutWidget.getDpY() - gapN;
newY = Math.max(newY, bounds.y);
scoutWidget.setY(newY);
}
}
}
}
break;
case HorizontalPack: {
NlComponent[] wArray = new NlComponent[widgetList.size()];
wArray = widgetList.toArray(wArray);
Arrays.sort(wArray, (w1, w2) -> Integer.compare(getDpX(w1), getDpX(w2)));
ScoutWidget[] list = ScoutWidget.getWidgetArray(widgetList.get(0).getParent());
Rectangle bounds = null;
for (int i = 0; i < wArray.length; i++) {
String id = SdkConstants.NEW_ID_PREFIX + wArray[i].getId();
ScoutWidget w = list[0].getChild(id);
if (bounds == null) {
bounds = new Rectangle(w.getRectangle());
}
else {
bounds = bounds.union(w.getRectangle());
}
}
for (NlComponent cw : wArray) {
for (ScoutWidget scoutWidget : list) {
if (scoutWidget.mNlComponent == cw) {
int gapW = scoutWidget.gap(Direction.LEFT, list);
int newX = margin + getDpX(scoutWidget.mNlComponent) - gapW;
newX = Math.max(newX, bounds.x);
scoutWidget.setX(newX);
}
}
}
}
break;
case ExpandVertically: {
expandVertically(scoutWidgets, parentScoutWidget, margin, true);
}
break;
case ExpandHorizontally: {
expandHorizontally(scoutWidgets, parentScoutWidget, margin, true);
}
break;
case ChainVerticalRemove:
case ChainHorizontalRemove:
case ChainVerticalMoveUp:
case ChainVerticalMoveDown:
case ChainHorizontalMoveLeft:
case ChainHorizontalMoveRight:
case ChainInsertHorizontal:
case ChainInsertVertical:
break; // cases covered by scout
}
}