use of com.android.tools.idea.uibuilder.model.SegmentType in project android by JetBrains.
the class ConstraintPainter method paintVerticalConstraint.
/**
* Paints a vertical constraint, handling the various scenarios where there are
* margins, or where the two nodes overlap horizontally and where they don't, etc.
* <p/>
* Here's an example of what will be shown for a "below" constraint where the
* nodes do not overlap horizontally and the target node has a bottom margin:
* <pre>
* +--------+
* | Target |
* +--------+
* |
* v
* - - - - - - - - - - - - - -
* ^
* |
* +--------+
* | Source |
* +--------+
* </pre>
*/
private static void paintVerticalConstraint(NlGraphics graphics, ConstraintType type, NlComponent sourceNode, Rectangle sourceBounds, NlComponent targetNode, Rectangle targetBounds, boolean highlightTargetEdge) {
SegmentType sourceSegmentTypeY = type.sourceSegmentTypeY;
SegmentType targetSegmentTypeY = type.targetSegmentTypeY;
Insets targetMargins = targetNode.getMargins();
assert sourceSegmentTypeY != SegmentType.UNKNOWN;
assert targetBounds != null;
int sourceY = sourceSegmentTypeY.getY(sourceNode, sourceBounds);
int targetY = targetSegmentTypeY == SegmentType.UNKNOWN ? sourceY : targetSegmentTypeY.getY(targetNode, targetBounds);
if (highlightTargetEdge && type.isRelativeToParentEdge()) {
graphics.useStyle(DROP_ZONE_ACTIVE);
graphics.fillRect(targetBounds.x, targetY - PARENT_RECT_SIZE / 2, targetBounds.width, PARENT_RECT_SIZE);
}
// First see if the two views overlap horizontally. If so, we can just draw a direct
// arrow from the source up to (or down to) the target.
//
// +--------+
// | Target |
// +--------+
// ^
// |
// |
// +--------+
// | Source |
// +--------+
//
int maxLeft = Math.max(sourceBounds.x, targetBounds.x);
int minRight = Math.min(x2(sourceBounds), x2(targetBounds));
int center = (maxLeft + minRight) / 2;
if (center > sourceBounds.x && center < x2(sourceBounds)) {
// for small margins
if (targetSegmentTypeY == SegmentType.BOTTOM && targetMargins.bottom > 5) {
int sharedY = targetY + targetMargins.bottom;
if (sourceY > sharedY + 2) {
// Skip when source falls on the margin line
graphics.useStyle(GUIDELINE_DASHED);
graphics.drawLine(targetBounds.x, sharedY, x2(targetBounds), sharedY);
graphics.useStyle(GUIDELINE);
graphics.drawArrow(center, sourceY, center, sharedY + 2);
graphics.drawArrow(center, targetY, center, sharedY - 3);
} else {
graphics.useStyle(GUIDELINE);
// Draw reverse arrow to make it clear the node is as close
// at it can be
graphics.drawArrow(center, targetY, center, sourceY);
}
return;
} else if (targetSegmentTypeY == SegmentType.TOP && targetMargins.top > 5) {
int sharedY = targetY - targetMargins.top;
if (sourceY < sharedY - 2) {
graphics.useStyle(GUIDELINE_DASHED);
graphics.drawLine(targetBounds.x, sharedY, x2(targetBounds), sharedY);
graphics.useStyle(GUIDELINE);
graphics.drawArrow(center, sourceY, center, sharedY - 3);
graphics.drawArrow(center, targetY, center, sharedY + 3);
} else {
graphics.useStyle(GUIDELINE);
graphics.drawArrow(center, targetY, center, sourceY);
}
return;
}
// direction
if (sourceY == targetY) {
if (sourceSegmentTypeY == SegmentType.BOTTOM || sourceSegmentTypeY == SegmentType.BASELINE) {
sourceY -= 2 * ARROW_SIZE;
} else if (sourceSegmentTypeY == SegmentType.TOP) {
sourceY += 2 * ARROW_SIZE;
} else {
assert sourceSegmentTypeY == SegmentType.CENTER_HORIZONTAL : sourceSegmentTypeY;
sourceY += sourceBounds.height / 2 - 2 * ARROW_SIZE;
}
} else if (sourceSegmentTypeY == SegmentType.BASELINE) {
sourceY = targetY - 2 * ARROW_SIZE;
}
// Center the vertical line in the overlap region
graphics.useStyle(GUIDELINE);
graphics.drawArrow(center, sourceY, center, targetY);
return;
}
// If there is no horizontal overlap in the vertical constraints, then we
// will show the attachment relative to a dashed line that extends beyond
// the target bounds, like this:
//
// +--------+
// | Target |
// +--------+ - - - - - - - - -
// ^
// |
// +--------+
// | Source |
// +--------+
//
// However, if the target node has a vertical margin, we may need to offset
// the line:
//
// +--------+
// | Target |
// +--------+
// |
// v
// - - - - - - - - - - - - - -
// ^
// |
// +--------+
// | Source |
// +--------+
//
// If not, we'll need to indicate a shared edge. This is the edge that separate
// them (but this will require me to evaluate margins!)
// Compute overlap region and pick the middle
int sharedY = targetSegmentTypeY == SegmentType.UNKNOWN ? sourceY : targetSegmentTypeY.getY(targetNode, targetBounds);
if (type.relativeToMargin) {
if (targetSegmentTypeY == SegmentType.TOP) {
sharedY -= targetMargins.top;
} else if (targetSegmentTypeY == SegmentType.BOTTOM) {
sharedY += targetMargins.bottom;
}
}
int startX;
int endX;
if (center <= sourceBounds.x) {
startX = targetBounds.x + targetBounds.width / 4;
endX = x2(sourceBounds);
} else {
assert (center >= x2(sourceBounds));
startX = sourceBounds.x;
endX = targetBounds.x + 3 * targetBounds.width / 4;
}
// Must draw segmented line instead
// Place the arrow 1/4 instead of 1/2 in the source to avoid overlapping with the
// selection handles
graphics.useStyle(GUIDELINE_DASHED);
graphics.drawLine(startX, sharedY, endX, sharedY);
// should point directly at the edge
if (Math.abs(sharedY - sourceY) < 2 * ARROW_SIZE) {
if (sourceSegmentTypeY == SegmentType.BASELINE) {
sourceY = sharedY - 2 * ARROW_SIZE;
} else if (sourceSegmentTypeY == SegmentType.TOP) {
sharedY = sourceY;
sourceY = sharedY + 2 * ARROW_SIZE;
} else {
sharedY = sourceY;
sourceY = sharedY - 2 * ARROW_SIZE;
}
}
graphics.useStyle(GUIDELINE);
// Draw the line from the source anchor to the shared edge
int x = sourceBounds.x + ((sourceSegmentTypeY == SegmentType.BASELINE) ? sourceBounds.width / 2 : sourceBounds.width / 4);
graphics.drawArrow(x, sourceY, x, sharedY);
// Draw the line from the target to the horizontal shared edge
int tx = centerX(targetBounds);
if (targetSegmentTypeY == SegmentType.TOP) {
int ty = targetBounds.y;
int margin = targetMargins.top;
if (margin == 0 || !type.relativeToMargin) {
graphics.drawArrow(tx, ty + 2 * ARROW_SIZE, tx, ty);
} else {
graphics.drawArrow(tx, ty, tx, ty - margin);
}
} else if (targetSegmentTypeY == SegmentType.BOTTOM) {
int ty = y2(targetBounds);
int margin = targetMargins.bottom;
if (margin == 0 || !type.relativeToMargin) {
graphics.drawArrow(tx, ty - 2 * ARROW_SIZE, tx, ty);
} else {
graphics.drawArrow(tx, ty, tx, ty + margin);
}
} else {
assert targetSegmentTypeY == SegmentType.BASELINE : targetSegmentTypeY;
int ty = targetSegmentTypeY.getY(targetNode, targetBounds);
graphics.drawArrow(tx, ty - 2 * ARROW_SIZE, tx, ty);
}
}
use of com.android.tools.idea.uibuilder.model.SegmentType in project android by JetBrains.
the class ConstraintPainter method paintCornerConstraint.
/**
* Paints a corner constraint, or returns false if this constraint is not a corner.
* A corner is one where there are two constraints from this source node to the
* same target node, one horizontal and one vertical, to the closest edges on
* the target node.
* <p/>
* Corners are a common occurrence. If we treat the horizontal and vertical
* constraints separately (below & toRightOf), then we end up with a lot of
* extra lines and arrows -- e.g. two shared edges and arrows pointing to these
* shared edges:
* <p/>
* <pre>
* +--------+ |
* | Target -->
* +----|---+ |
* v
* - - - - - -|- - - - - -
* ^
* | +---|----+
* <-- Source |
* | +--------+
*
* Instead, we can simply draw a diagonal arrow here to represent BOTH constraints and
* reduce clutter:
*
* +---------+
* | Target _|
* +-------|\+
* \
* \--------+
* | Source |
* +--------+
* </pre>
*
* @param graphics the graphics context to draw
* @param type the constraint to be drawn
* @param sourceNode the source node
* @param sourceBounds the bounds of the source node
* @param targetNode the target node
* @param targetBounds the bounds of the target node
* @param allConstraints the set of all constraints; if a corner is found and painted the
* matching corner constraint is removed from the set
* @return true if the constraint was handled and painted as a corner, false otherwise
*/
@SuppressWarnings("PointlessArithmeticExpression")
private static boolean paintCornerConstraint(NlGraphics graphics, ConstraintType type, NlComponent sourceNode, Rectangle sourceBounds, NlComponent targetNode, Rectangle targetBounds, Set<DependencyGraph.Constraint> allConstraints, TextDirection textDirection) {
SegmentType sourceSegmentTypeX = type.sourceSegmentTypeX;
SegmentType sourceSegmentTypeY = type.sourceSegmentTypeY;
SegmentType targetSegmentTypeX = type.targetSegmentTypeX;
SegmentType targetSegmentTypeY = type.targetSegmentTypeY;
ConstraintType opposite1, opposite2;
switch(type) {
case LAYOUT_BELOW:
case LAYOUT_ABOVE:
opposite1 = ConstraintType.LAYOUT_LEFT_OF;
opposite2 = ConstraintType.LAYOUT_RIGHT_OF;
break;
case LAYOUT_LEFT_OF:
case LAYOUT_RIGHT_OF:
opposite1 = ConstraintType.LAYOUT_ABOVE;
opposite2 = ConstraintType.LAYOUT_BELOW;
break;
default:
return false;
}
DependencyGraph.Constraint pair = null;
for (DependencyGraph.Constraint constraint : allConstraints) {
if ((constraint.type == opposite1 || constraint.type == opposite2) && constraint.to.node == targetNode && constraint.from.node == sourceNode) {
pair = constraint;
break;
}
}
if (pair != null) {
// Visualize the corner constraint
if (sourceSegmentTypeX == SegmentType.UNKNOWN) {
sourceSegmentTypeX = pair.type.sourceSegmentTypeX;
}
if (sourceSegmentTypeY == SegmentType.UNKNOWN) {
sourceSegmentTypeY = pair.type.sourceSegmentTypeY;
}
if (targetSegmentTypeX == SegmentType.UNKNOWN) {
targetSegmentTypeX = pair.type.targetSegmentTypeX;
}
if (targetSegmentTypeY == SegmentType.UNKNOWN) {
targetSegmentTypeY = pair.type.targetSegmentTypeY;
}
int x1, y1, x2, y2;
if (textDirection.isLeftSegment(sourceSegmentTypeX)) {
x1 = sourceBounds.x + 1 * sourceBounds.width / 4;
} else {
x1 = sourceBounds.x + 3 * sourceBounds.width / 4;
}
if (sourceSegmentTypeY == SegmentType.TOP) {
y1 = sourceBounds.y + 1 * sourceBounds.height / 4;
} else {
y1 = sourceBounds.y + 3 * sourceBounds.height / 4;
}
if (textDirection.isLeftSegment(targetSegmentTypeX)) {
x2 = targetBounds.x + 1 * targetBounds.width / 4;
} else {
x2 = targetBounds.x + 3 * targetBounds.width / 4;
}
if (targetSegmentTypeY == SegmentType.TOP) {
y2 = targetBounds.y + 1 * targetBounds.height / 4;
} else {
y2 = targetBounds.y + 3 * targetBounds.height / 4;
}
graphics.useStyle(GUIDELINE);
graphics.drawArrow(x1, y1, x2, y2);
// Don't process this constraint on its own later.
allConstraints.remove(pair);
return true;
}
return false;
}
use of com.android.tools.idea.uibuilder.model.SegmentType in project android by JetBrains.
the class ConstraintPainter method paintConstraint.
/**
* Paints a constraint of the given type from the given source node, to the
* given target node, with the specified bounds.
*/
private static void paintConstraint(NlGraphics graphics, ConstraintType type, NlComponent sourceNode, Rectangle sourceBounds, NlComponent targetNode, Rectangle targetBounds, @Nullable Set<DependencyGraph.Constraint> allConstraints, boolean highlightTargetEdge, TextDirection textDirection) {
SegmentType sourceSegmentTypeX = type.sourceSegmentTypeX;
SegmentType sourceSegmentTypeY = type.sourceSegmentTypeY;
SegmentType targetSegmentTypeX = type.targetSegmentTypeX;
SegmentType targetSegmentTypeY = type.targetSegmentTypeY;
// Horizontal center constraint?
if (sourceSegmentTypeX == SegmentType.CENTER_VERTICAL && targetSegmentTypeX == SegmentType.CENTER_VERTICAL) {
paintHorizontalCenterConstraint(graphics, sourceBounds, targetBounds);
return;
}
// Vertical center constraint?
if (sourceSegmentTypeY == SegmentType.CENTER_HORIZONTAL && targetSegmentTypeY == SegmentType.CENTER_HORIZONTAL) {
paintVerticalCenterConstraint(graphics, sourceBounds, targetBounds);
return;
}
// Corner constraint?
if (allConstraints != null && (type == ConstraintType.LAYOUT_ABOVE || type == ConstraintType.LAYOUT_BELOW || type == ConstraintType.LAYOUT_LEFT_OF || type == ConstraintType.LAYOUT_RIGHT_OF)) {
if (paintCornerConstraint(graphics, type, sourceNode, sourceBounds, targetNode, targetBounds, allConstraints, textDirection)) {
return;
}
}
// Vertical constraint?
if (sourceSegmentTypeX == SegmentType.UNKNOWN) {
paintVerticalConstraint(graphics, type, sourceNode, sourceBounds, targetNode, targetBounds, highlightTargetEdge);
return;
}
// Horizontal constraint?
if (sourceSegmentTypeY == SegmentType.UNKNOWN) {
paintHorizontalConstraint(graphics, type, sourceNode, sourceBounds, targetNode, targetBounds, highlightTargetEdge, textDirection);
return;
}
// and is not a centering constraint
assert false;
}
use of com.android.tools.idea.uibuilder.model.SegmentType in project android by JetBrains.
the class ConstraintPainter method paintHorizontalConstraint.
/**
* Paints a horizontal constraint, handling the various scenarios where there are margins,
* or where the two nodes overlap horizontally and where they don't, etc.
*/
private static void paintHorizontalConstraint(NlGraphics graphics, ConstraintType type, NlComponent sourceNode, Rectangle sourceBounds, NlComponent targetNode, Rectangle targetBounds, boolean highlightTargetEdge, TextDirection textDirection) {
SegmentType sourceSegmentTypeX = type.sourceSegmentTypeX;
SegmentType targetSegmentTypeX = type.targetSegmentTypeX;
Insets targetMargins = targetNode.getMargins();
assert sourceSegmentTypeX != SegmentType.UNKNOWN;
assert targetBounds != null;
// See paintVerticalConstraint for explanations of the various cases.
int sourceX = sourceSegmentTypeX.getX(textDirection, sourceNode, sourceBounds);
int targetX = targetSegmentTypeX == SegmentType.UNKNOWN ? sourceX : targetSegmentTypeX.getX(textDirection, targetNode, targetBounds);
if (highlightTargetEdge && type.isRelativeToParentEdge()) {
graphics.useStyle(DROP_ZONE_ACTIVE);
graphics.fillRect(targetX - PARENT_RECT_SIZE / 2, targetBounds.y, PARENT_RECT_SIZE, targetBounds.height);
}
int maxTop = Math.max(sourceBounds.y, targetBounds.y);
int minBottom = Math.min(y2(sourceBounds), y2(targetBounds));
// First see if the two views overlap vertically. If so, we can just draw a direct
// arrow from the source over to the target.
int center = (maxTop + minBottom) / 2;
if (center > sourceBounds.y && center < y2(sourceBounds)) {
// See if we should draw a margin line
if (textDirection.isRightSegment(targetSegmentTypeX) && targetMargins.right > 5) {
int sharedX = targetX + targetMargins.right;
if (sourceX > sharedX + 2) {
// Skip when source falls on the margin line
graphics.useStyle(GUIDELINE_DASHED);
graphics.drawLine(sharedX, targetBounds.y, sharedX, y2(targetBounds));
graphics.useStyle(GUIDELINE);
graphics.drawArrow(sourceX, center, sharedX + 2, center);
graphics.drawArrow(targetX, center, sharedX - 3, center);
} else {
graphics.useStyle(GUIDELINE);
// Draw reverse arrow to make it clear the node is as close
// at it can be
graphics.drawArrow(targetX, center, sourceX, center);
}
return;
} else if (textDirection.isLeftSegment(targetSegmentTypeX) && targetMargins.left > 5) {
int sharedX = targetX - targetMargins.left;
if (sourceX < sharedX - 2) {
graphics.useStyle(GUIDELINE_DASHED);
graphics.drawLine(sharedX, targetBounds.y, sharedX, y2(targetBounds));
graphics.useStyle(GUIDELINE);
graphics.drawArrow(sourceX, center, sharedX - 3, center);
graphics.drawArrow(targetX, center, sharedX + 3, center);
} else {
graphics.useStyle(GUIDELINE);
graphics.drawArrow(targetX, center, sourceX, center);
}
return;
}
if (sourceX == targetX) {
if (textDirection.isRightSegment(sourceSegmentTypeX)) {
sourceX -= 2 * ARROW_SIZE;
} else if (textDirection.isLeftSegment(sourceSegmentTypeX)) {
sourceX += 2 * ARROW_SIZE;
} else {
assert sourceSegmentTypeX == SegmentType.CENTER_VERTICAL : sourceSegmentTypeX;
sourceX += sourceBounds.width / 2 - 2 * ARROW_SIZE;
}
}
graphics.useStyle(GUIDELINE);
graphics.drawArrow(sourceX, center, targetX, center);
return;
}
// Segment line
// Compute overlap region and pick the middle
int sharedX = targetSegmentTypeX == SegmentType.UNKNOWN ? sourceX : targetSegmentTypeX.getX(textDirection, targetNode, targetBounds);
if (type.relativeToMargin) {
if (textDirection.isLeftSegment(targetSegmentTypeX)) {
sharedX -= targetMargins.left;
} else if (textDirection.isRightSegment(targetSegmentTypeX)) {
sharedX += targetMargins.right;
}
}
int startY, endY;
if (center <= sourceBounds.y) {
startY = targetBounds.y + targetBounds.height / 4;
endY = y2(sourceBounds);
} else {
assert (center >= y2(sourceBounds));
startY = sourceBounds.y;
endY = targetBounds.y + 3 * targetBounds.height / 2;
}
// Must draw segmented line instead
// Place at 1/4 instead of 1/2 to avoid overlapping with selection handles
int y = sourceBounds.y + sourceBounds.height / 4;
graphics.useStyle(GUIDELINE_DASHED);
graphics.drawLine(sharedX, startY, sharedX, endY);
// should point directly at the edge
if (Math.abs(sharedX - sourceX) < 2 * ARROW_SIZE) {
if (textDirection.isLeftSegment(sourceSegmentTypeX)) {
sharedX = sourceX;
sourceX = sharedX + 2 * ARROW_SIZE;
} else {
sharedX = sourceX;
sourceX = sharedX - 2 * ARROW_SIZE;
}
}
graphics.useStyle(GUIDELINE);
// Draw the line from the source anchor to the shared edge
graphics.drawArrow(sourceX, y, sharedX, y);
// Draw the line from the target to the horizontal shared edge
int ty = centerY(targetBounds);
if (textDirection.isLeftSegment(targetSegmentTypeX)) {
int tx = targetBounds.x;
int margin = targetMargins.left;
if (margin == 0 || !type.relativeToMargin) {
graphics.drawArrow(tx + 2 * ARROW_SIZE, ty, tx, ty);
} else {
graphics.drawArrow(tx, ty, tx - margin, ty);
}
} else {
assert textDirection.isRightSegment(targetSegmentTypeX);
int tx = x2(targetBounds);
int margin = targetMargins.right;
if (margin == 0 || !type.relativeToMargin) {
graphics.drawArrow(tx - 2 * ARROW_SIZE, ty, tx, ty);
} else {
graphics.drawArrow(tx, ty, tx + margin, ty);
}
}
}
Aggregations