in designer/src/com/android/tools/idea/uibuilder/handlers/constraint/draw/DrawConnection.java [292:810]
public static boolean draw(Graphics2D g,
ColorSet color,
ScenePicker.Writer picker,
SecondarySelector secondarySelector,
int connectionType,
@SwingCoordinate Rectangle source,
int sourceDirection,
@SwingCoordinate Rectangle dest,
int destDirection,
int myDestType,
int margin,
@SwingCoordinate int marginDistance,
boolean isMarginReference,
int modeFrom,
int modeTo,
long stateChange) {
boolean animate = false;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Color constraintColor = modeGetConstraintsColor(modeTo, color);
Color marginColor = modeGetMarginColor(modeTo, color);
long timeSince = System.nanoTime() - stateChange;
boolean hover = (HOVER_FLAG & modeTo) > 0 || modeTo == MODE_CONSTRAINT_SELECTED;
modeTo &= HOVER_MASK;
if (timeSince < TRANSITION_TIME) {
float t = (float)((timeSince) / (double)TRANSITION_TIME);
Color fromColor = modeGetConstraintsColor(modeFrom, color);
Color toColor = modeGetConstraintsColor(modeTo, color);
constraintColor = interpolate(fromColor, toColor, t);
animate = true;
}
if (connectionType == TYPE_BASELINE) {
Color hoverColor = modeGetConstraintsColor(MODE_WILL_HOVER, color);
drawBaseLine(g, source, dest, constraintColor, hoverColor, picker, secondarySelector, hover);
return animate;
}
int startx = getConnectionX(sourceDirection, source);
int starty = getConnectionY(sourceDirection, source);
int endx = getConnectionX(destDirection, dest);
int endy = getConnectionY(destDirection, dest);
int dx = getDestinationDX(destDirection);
int dy = getDestinationDY(destDirection);
int manhattanDistance = Math.abs(startx - endx) + Math.abs(starty - endy);
int scale_source = Math.min(90, manhattanDistance);
int scale_dest = scale_source;
boolean flip_arrow = false;
if (myDestType != DEST_NORMAL) {
switch (destDirection) {
case DIR_BOTTOM:
case DIR_TOP:
endx = startx;
break;
case DIR_LEFT:
case DIR_RIGHT:
endy = starty;
break;
}
}
if (sourceDirection == destDirection) {
switch (myDestType) {
case DEST_PARENT:
scale_dest *= -1;
flip_arrow = true;
switch (destDirection) {
case DIR_BOTTOM:
case DIR_TOP:
dy *= -1;
break;
case DIR_LEFT:
case DIR_RIGHT:
dx *= -1;
break;
}
break;
case DEST_NORMAL:
switch (destDirection) {
case DIR_BOTTOM:
if (endy - 1 > starty) {
scale_dest *= -1;
dy *= -1;
flip_arrow = true;
}
break;
case DIR_TOP:
if (endy < starty) {
scale_dest *= -1;
dy *= -1;
flip_arrow = true;
}
break;
case DIR_LEFT:
if (endx < startx) {
scale_dest *= -1;
dx *= -1;
flip_arrow = true;
}
break;
case DIR_RIGHT:
if (endx - 1 > startx) {
scale_dest *= -1;
dx *= -1;
flip_arrow = true;
}
break;
}
break;
}
}
int[] xPoints = new int[3];
int[] yPoints = new int[3];
int dir = flip_arrow ? ourOppositeDirection[destDirection] : destDirection;
ourPath.reset();
ourPath.moveTo(startx, starty);
Stroke defaultStroke;
if (manhattanDistance == 0) {
g.setColor(constraintColor);
DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
g.fillPolygon(xPoints, yPoints, 3);
g.draw(ourPath);
ourPath.reset();
ourPath.moveTo(startx, starty);
}
defaultStroke = g.getStroke();
g.setStroke(getStroke(StrokeType.NORMAL, false, modeTo));
switch (connectionType) {
case TYPE_CHAIN:
g.setColor(constraintColor);
// Draw curved line twice mirroring so it looks like a chain
DrawConnectionUtils
.drawChain(g, hover, startx, starty, endx, endy, scale_source, scale_dest, sourceDirection, destDirection, picker,
secondarySelector, color, modeTo);
DrawConnectionUtils
.drawChain(g, hover, endx, endy, startx, starty, scale_source, scale_dest, destDirection, sourceDirection, picker,
secondarySelector, color, modeTo);
if (modeTo == MODE_DELETING) {
DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
g.fillPolygon(xPoints, yPoints, 3);
}
break;
case TYPE_ADJACENT:
g.setColor(constraintColor);
DrawConnectionUtils.getSmallArrow(dir, startx - dx / 2, starty - dy / 2, xPoints, yPoints);
g.fillPolygon(xPoints, yPoints, 3);
if (destDirection == DIR_LEFT || destDirection == DIR_RIGHT) {
startx = (startx + endx) / 2;
endx = startx;
}
else {
starty = (starty + endy) / 2;
endy = starty;
}
g.drawLine(startx, starty, endx, endy);
if (picker != null && secondarySelector != null) {
picker.addLine(secondarySelector, 4, startx, starty, endx, endy, 4);
}
break;
case TYPE_SPRING:
boolean drawArrow = true;
int springEndX = endx;
int springEndY = endy;
if (myDestType != DEST_NORMAL) {
int rectGap = scale(4);
int rectDim = scale(9);
if (margin != 0) {
String marginString = Integer.toString(margin);
if (destDirection == DIR_LEFT || destDirection == DIR_RIGHT) {
int gap = Math.max(marginDistance, DrawConnectionUtils.getHorizontalMarginGap(g, marginString));
if (Math.abs(startx - endx) < gap) {
// Doesn't have enough space to paint margin string.
marginString = null;
}
int marginX = endx - ((endx > startx) ? gap : -gap);
int arrow = ((endx > startx) ? 1 : -1) * DrawConnectionUtils.ARROW_SIDE;
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
g.drawLine(marginX, endy, endx - arrow, endy);
g.setStroke(tmpStroke);
}
if (picker != null && secondarySelector != null) {
picker.addLine(secondarySelector, 8, marginX, endy, endx - arrow, endy, 4);
}
g.setColor(constraintColor);
g.drawLine(marginX, endy, endx - arrow, endy);
DrawConnectionUtils.drawHorizontalMarginString(g, marginColor, marginString, isMarginReference, marginX, endx - arrow, endy);
springEndX = marginX;
}
else {
int gap = Math.max(marginDistance, DrawConnectionUtils.getVerticalMarginGap(g));
if (Math.abs(starty - endy) < gap) {
// Doesn't have enough space to paint margin string.
marginString = null;
}
int marginY = endy - ((endy > starty) ? gap : -gap);
int arrow = ((endy > starty) ? 1 : -1) * DrawConnectionUtils.ARROW_SIDE;
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
g.drawLine(endx, marginY, endx, endy - arrow);
g.setStroke(tmpStroke);
}
if (picker != null && secondarySelector != null) {
picker.addLine(secondarySelector, 8, endx, marginY, endx, endy - arrow, 4);
}
g.setColor(constraintColor);
g.drawLine(endx, marginY, endx, endy - arrow);
DrawConnectionUtils.drawVerticalMarginString(g, marginColor, marginString, isMarginReference, endx, marginY, endy - arrow);
springEndY = marginY;
}
}
if (endx == startx) {
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
g.drawLine(startx, starty, startx, springEndY);
g.setStroke(tmpStroke);
}
g.setColor(constraintColor);
DrawConnectionUtils.drawVerticalZigZagLine(ourPath, startx, starty, springEndY);
if (picker != null && secondarySelector != null) {
picker.addLine(secondarySelector, 8, startx, starty, startx, springEndY, 4);
}
g.fillRect(startx - rectGap, springEndY, rectDim, 1);
}
else {
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
g.drawLine(startx, endy, springEndX, endy);
g.setStroke(tmpStroke);
}
g.setColor(constraintColor);
DrawConnectionUtils.drawHorizontalZigZagLine(ourPath, startx, springEndX, endy);
if (picker != null && secondarySelector != null) {
picker.addLine(secondarySelector, 8, startx, endy, springEndX, endy, 4);
}
g.fillRect(springEndX, endy - rectGap, 1, rectDim);
}
}
else {
g.setColor(constraintColor);
int rectGap = scale(2);
int rectDim = scale(5);
if (destDirection == DIR_LEFT || destDirection == DIR_RIGHT) {
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
GeneralPath hoverPath = new GeneralPath(ourPath);
hoverPath.moveTo(startx, starty);
hoverPath.lineTo(endx, starty);
hoverPath.lineTo(endx, endy);
g.draw(hoverPath);
g.setStroke(tmpStroke);
}
g.setColor(constraintColor);
DrawConnectionUtils.drawHorizontalZigZagLine(ourPath, startx, endx, starty);
if (picker != null && secondarySelector != null) {
picker.addLine(secondarySelector, 8, startx, starty, endx, starty, 4);
picker.addLine(secondarySelector, 8, endx, starty, endx, endy, 4);
}
g.setStroke(getStroke(StrokeType.SPRING, false, modeTo));
drawArrow = false;
g.drawLine(endx, starty, endx, endy);
g.fillRoundRect(endx - rectGap, endy - rectGap, rectDim, rectDim, rectGap, rectGap);
}
else {
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
GeneralPath hoverPath = new GeneralPath(ourPath);
hoverPath.moveTo(startx, starty);
hoverPath.lineTo(startx, endy);
hoverPath.lineTo(endx, endy);
g.draw(hoverPath);
g.setStroke(tmpStroke);
}
g.setColor(constraintColor);
DrawConnectionUtils.drawVerticalZigZagLine(ourPath, startx, starty, endy);
if (picker != null && secondarySelector != null) {
picker.addLine(secondarySelector, 8, startx, starty, startx, endy, 4);
picker.addLine(secondarySelector, 8, startx, endy, endx, endy, 4);
}
g.setStroke(getStroke(StrokeType.SPRING, false, modeTo));
drawArrow = false;
g.drawLine(startx, endy, endx, endy);
g.fillRoundRect(endx - rectGap, endy - rectGap, rectDim, rectDim, rectGap, rectGap);
}
}
g.setStroke(getStroke(StrokeType.NORMAL, false, modeTo));
g.setColor(constraintColor);
g.draw(ourPath);
if (drawArrow) {
g.setColor(constraintColor);
DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
g.fillPolygon(xPoints, yPoints, 3);
}
break;
case TYPE_CENTER:
case TYPE_CENTER_WIDGET:
int dir0_x = 0, dir0_y = 0; // direction of the start
int dir1_x = 0, dir1_y = 0; // direction the arch must go
int dir2_x = 0, dir2_y = 0;
int p6x, p6y; // position of the 6'th point on the curve
if (destDirection == DIR_LEFT || destDirection == DIR_RIGHT) {
dir0_x = (sourceDirection == DIR_LEFT) ? -1 : 1;
dir1_y = (endy > starty) ? 1 : -1;
dir2_x = (destDirection == DIR_LEFT) ? -1 : 1;
p6x = (destDirection == DIR_LEFT)
? endx - GAP * 2
: endx + GAP * 2;
p6y = starty + dir0_y * GAP + (source.height / 2 + GAP) * dir1_y;
int vline_y1 = -1, vline_y2 = -1;
if (source.y > dest.y + dest.height) {
vline_y1 = dest.y + dest.height;
vline_y2 = source.y;
}
if (source.y + source.height < dest.y) {
vline_y1 = source.y + source.height;
vline_y2 = dest.y;
}
if (vline_y1 != -1) {
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
int xpos = source.x + source.width / 2;
g.drawLine(xpos, vline_y1, xpos, vline_y2);
g.setStroke(tmpStroke);
}
g.setStroke(getStroke(StrokeType.DASH, false, modeTo));
int xpos = source.x + source.width / 2;
g.setColor(constraintColor);
g.drawLine(xpos, vline_y1, xpos, vline_y2);
}
}
else {
dir1_x = (endx > startx) ? 1 : -1;
dir0_y = (sourceDirection == DIR_TOP) ? -1 : 1;
dir2_y = (destDirection == DIR_TOP) ? -1 : 1;
p6y = (destDirection == DIR_TOP)
? endy - GAP * 2
: endy + GAP * 2;
p6x = startx + dir0_x * GAP + (source.width / 2 + GAP) * dir1_x;
int vline_x1 = -1, vline_x2 = -1;
if (source.x > dest.x + dest.width) {
vline_x1 = dest.x + dest.width;
vline_x2 = source.x;
}
if (source.x + source.width < dest.x) {
vline_x1 = source.x + source.width;
vline_x2 = dest.x;
}
if (vline_x1 != -1) {
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
int ypos = source.y + source.height / 2;
g.drawLine(vline_x1, ypos, vline_x2, ypos);
g.setStroke(tmpStroke);
}
g.setStroke(getStroke(StrokeType.DASH, false, modeTo));
int ypos = source.y + source.height / 2;
g.setColor(constraintColor);
g.drawLine(vline_x1, ypos, vline_x2, ypos);
}
}
int len = 6;
int[] px = new int[len];
int[] py = new int[len];
px[0] = startx;
py[0] = starty;
px[1] = startx + dir0_x * GAP;
py[1] = starty + dir0_y * GAP;
px[2] = px[1] + (source.width / 2 + GAP) * dir1_x;
py[2] = py[1] + (source.height / 2 + GAP) * dir1_y;
px[3] = p6x;
py[3] = p6y;
px[4] = endx + 2 * dir2_x * GAP;
py[4] = endy + 2 * dir2_y * GAP;
px[5] = endx;
py[5] = endy;
if (TYPE_CENTER_WIDGET == connectionType) {
len = DrawConnectionUtils.removeZigZag(px, py, len, 50);
}
DrawConnectionUtils.drawRound(ourPath, px, py, len, GAP);
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
g.draw(ourPath);
g.setStroke(tmpStroke);
}
if (picker != null && secondarySelector != null) {
DrawConnectionUtils.drawPick(picker, secondarySelector, px, py, len, GAP);
}
g.setStroke(getStroke(StrokeType.NORMAL, false, modeTo));
g.setColor(constraintColor);
DrawConnectionUtils.getArrow(destDirection, endx, endy, xPoints, yPoints);
g.fillPolygon(xPoints, yPoints, 3);
g.draw(ourPath);
break;
case TYPE_NORMAL:
if (margin > 0) {
if (sourceDirection == DIR_RIGHT || sourceDirection == DIR_LEFT) {
boolean above = starty < endy;
int line_y = starty + (above ? -1 : 1) * source.height / 4;
g.setColor(marginColor);
DrawConnectionUtils.drawHorizontalMarginIndicator(g, String.valueOf(margin), isMarginReference, startx, endx, line_y);
if (myDestType != DEST_PARENT || (line_y < dest.y || line_y > dest.y + dest.height)) {
int constraintX = (destDirection == DIR_LEFT) ? dest.x : dest.x + dest.width;
g.setStroke(getStroke(StrokeType.DASH, false, modeTo));
int overlap = (above) ? -OVER_HANG : OVER_HANG;
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
g.drawLine(constraintX, line_y + overlap, constraintX, above ? dest.y : dest.y + dest.height);
g.setStroke(tmpStroke);
}
g.setColor(constraintColor);
g.drawLine(constraintX, line_y + overlap, constraintX, above ? dest.y : dest.y + dest.height);
}
}
else {
boolean left = startx < endx;
int line_x = startx + (left ? -1 : 1) * source.width / 4;
g.setColor(marginColor);
DrawConnectionUtils.drawVerticalMarginIndicator(g, String.valueOf(margin), isMarginReference, line_x, starty, endy);
if (myDestType != DEST_PARENT || (line_x < dest.x || line_x > dest.x + dest.width)) {
int constraint_y = (destDirection == DIR_TOP) ? dest.y : dest.y + dest.height;
g.setStroke(getStroke(StrokeType.DASH, false, modeTo));
int overlap = (left) ? -OVER_HANG : OVER_HANG;
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
g.drawLine(line_x + overlap, constraint_y,
left ? dest.x : dest.x + dest.width, constraint_y);
g.setStroke(tmpStroke);
}
g.setColor(constraintColor);
g.drawLine(line_x + overlap, constraint_y,
left ? dest.x : dest.x + dest.width, constraint_y);
}
}
}
g.setStroke(getStroke(StrokeType.NORMAL, false, modeTo));
if (startx - endx == 0 && (sourceDirection != destDirection || dirDeltaX[sourceDirection] == 0)) {
// Case for straight vertical lines or adjacent widgets connecting opposite horizontal anchors
scale_source = 0;
scale_dest = 0;
}
else if (starty - endy == 0 && (sourceDirection != destDirection || dirDeltaY[sourceDirection] == 0)) {
// Case for straight horizontal lines or adjacent widgets connecting opposite vertical anchors
scale_source = 0;
scale_dest = 0;
}
if (sourceDirection == destDirection && margin == 0) {
// For adjacent widgets connecting the same anchor
scale_source /= 3;
scale_dest /= 2;
}
float x1 = startx, y1 = starty, x2, y2, x3, y3, x4, y4;
if (hover) {
g.setColor(modeGetConstraintsColor(MODE_WILL_HOVER, color));
Stroke tmpStroke = g.getStroke();
g.setStroke(myHoverStroke);
GeneralPath hoverPath = new GeneralPath(ourPath);
hoverPath
.curveTo(startx + scale_source * dirDeltaX[sourceDirection], starty + scale_source * dirDeltaY[sourceDirection],
endx + dx + scale_dest * dirDeltaX[destDirection], endy + dy + scale_dest * dirDeltaY[destDirection],
endx + dx, endy + dy);
g.draw(hoverPath);
g.setStroke(tmpStroke);
}
g.setColor(constraintColor);
ourPath.curveTo(x2 = startx + scale_source * dirDeltaX[sourceDirection], y2 = starty + scale_source * dirDeltaY[sourceDirection],
x3 = endx + dx + scale_dest * dirDeltaX[destDirection], y3 = endy + dy + scale_dest * dirDeltaY[destDirection],
x4 = endx + dx, y4 = endy + dy);
if (picker != null && secondarySelector != null) {
picker.addCurveTo(secondarySelector, 4, (int)x1, (int)y1, (int)x2, (int)y2, (int)x3, (int)y3, (int)x4, (int)y4, 4);
}
g.setStroke(getStroke(StrokeType.BACKGROUND, false, modeTo));
g.setColor(color.getBackground());
DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
g.fillPolygon(xPoints, yPoints, 3);
g.draw(ourPath);
g.setColor(constraintColor);
DrawConnectionUtils.getArrow(dir, endx, endy, xPoints, yPoints);
g.fillPolygon(xPoints, yPoints, 3);
g.draw(ourPath);
}
g.setStroke(defaultStroke);
return animate;
}