in designer/src/com/android/tools/idea/uibuilder/handlers/constraint/draw/ConstraintLayoutDecorator.java [356:558]
private static void buildListConnections(@NotNull DisplayList list,
long time,
@NotNull SceneContext sceneContext,
@NotNull SceneComponent constraintComponent,
@NotNull SceneComponent child) {
Rectangle dest_rect = new Rectangle();
Rectangle source_rect = new Rectangle();
child.fillDrawRect(time, source_rect);
convert(sceneContext, source_rect);
ConnectionStatus connectStatus = new ConnectionStatus();
List<NlComponent> selection = constraintComponent.getScene().getSelection();
NlComponent component = child.getNlComponent();
int hover = -1;
Object hover_obj = component.getClientProperty(CONSTRAINT_HOVER);
if (hover_obj != null && hover_obj instanceof SecondarySelector.Constraint) {
SecondarySelector.Constraint constraint = ((SecondarySelector.Constraint)hover_obj);
hover = constraint.ordinal();
}
// get the Secondary selection
Object ss = constraintComponent.getScene().getSecondarySelection();
int selectedDirection = -1;
if (ss != null && ss instanceof SecondarySelector.Constraint) {
SecondarySelector.Constraint constraint = ((SecondarySelector.Constraint)ss);
selectedDirection = constraint.ordinal();
}
boolean constraintSelected = selectedDirection != -1;
boolean anyViewSelected = selection != null && !selection.isEmpty();
boolean fade = ConstraintLayoutHandler.getVisualProperty(ConstraintLayoutHandler.FADE_UNSELECTED_VIEWS);
if (fade && selection.isEmpty()) { // nothing selected do not fade
fade = false;
}
if (fade && selection.contains(component) && selection.size() == 1) { // only parent selected don't fade
fade = false;
}
boolean viewSelected = selection.contains(child.getNlComponent());
long changeStart;
connectStatus.getConnectionInfo(child.getNlComponent(), viewSelected);
// Extract Scene Components constraints from cache (Table speeds up next step)
ConnectionType[] connectionTypes = new ConnectionType[ourDirections.length];
SceneComponent[] connectionTo = new SceneComponent[ourDirections.length];
for (int i = 0; i < ourDirections.length; i++) {
connectionTypes[i] = (ConnectionType)child.myCache.get(ourDirectionsType[i]);
Object obj = child.myCache.get(ourDirections[i]);
connectionTo[i] = (obj instanceof SceneComponent) ? (SceneComponent)obj : null;
}
for (int i = 0; i < ourDirections.length; i++) { // For each direction (not including baseline
boolean selectedConnection = (selectedDirection == i && viewSelected);
boolean hoverConnection = hover == i;
ConnectionType type = connectionTypes[i];
SceneComponent sc = connectionTo[i];
int destType = DrawConnection.DEST_NORMAL;
if (sc != null) {
sc.fillDrawRect(time, dest_rect); // get the destination rectangle
convert(sceneContext, dest_rect); // scale to screen space
int connect = (type == ConnectionType.SAME) ? i : ourOppositeDirection[i];
if (child.getParent().equals(sc)) { // flag a child connection
destType = DrawConnection.DEST_PARENT;
}
else if (AndroidXConstants.CONSTRAINT_LAYOUT_GUIDELINE.isEqualsIgnoreCase(NlComponentHelperKt.getComponentClassName(sc.getNlComponent()))
||
AndroidXConstants.CONSTRAINT_LAYOUT_BARRIER
.isEqualsIgnoreCase(NlComponentHelperKt.getComponentClassName(sc.getNlComponent()))) {
destType = DrawConnection.DEST_GUIDELINE;
}
int connectType = DrawConnection.TYPE_NORMAL;
if (connectionTo[ourOppositeDirection[i]] != null) { // opposite side is connected
connectType = DrawConnection.TYPE_SPRING;
if (connectionTo[ourOppositeDirection[i]] == sc && destType != DrawConnection.DEST_PARENT) { // center
if (connectionTypes[ourOppositeDirection[i]] != type) {
connectType = DrawConnection.TYPE_CENTER;
}
else {
connectType = DrawConnection.TYPE_CENTER_WIDGET;
}
}
}
SceneComponent toComponentsTo = (SceneComponent)sc.myCache.get(ourDirections[connect]);
// Chain detection
if (type == ConnectionType.BACKWARD // this connection must be backward
&& toComponentsTo == child // it must connect to some one who connects to me
&& sc.myCache.get(ourDirectionsType[connect]) == ConnectionType.BACKWARD) { // and that connection must be backward as well
connectType = DrawConnection.TYPE_CHAIN;
if (sc.myCache.containsKey(ourChainDirections[ourOppositeDirection[i]])) {
continue; // no need to add element to display list chains only have to go one way
}
}
int margin = 0;
int marginDistance = 0;
boolean isMarginReference = false;
float bias = 0.5f;
boolean rtl = constraintComponent.getScene().isInRTL();
String[] margin_attr = (rtl) ? MARGIN_ATTR_RTL[i] : MARGIN_ATTR_LTR[i];
String marginString = child.getAuthoritativeNlComponent().getLiveAttribute(SdkConstants.ANDROID_URI, margin_attr[0]);
if (marginString == null && margin_attr.length > 1) {
marginString = child.getAuthoritativeNlComponent().getLiveAttribute(SdkConstants.ANDROID_URI, margin_attr[1]);
}
if (marginString == null) {
if (i == 0) { // left check if it is start
marginString =
child.getAuthoritativeNlComponent().getLiveAttribute(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_MARGIN_START);
}
else if (i == 1) { // right check if it is end
marginString =
child.getAuthoritativeNlComponent().getLiveAttribute(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_MARGIN_END);
}
}
if (marginString != null) {
if (marginString.startsWith("@")) {
isMarginReference = true;
}
margin = ConstraintUtilities.getDpValue(child.getAuthoritativeNlComponent(), marginString);
marginDistance = sceneContext.getSwingDimensionDip(margin);
}
String biasString = child.getAuthoritativeNlComponent().getLiveAttribute(SdkConstants.SHERPA_URI, BIAS_ATTR[i]);
if (biasString != null) {
try {
bias = Float.parseFloat(biasString);
if (FLIP_BIAS[i]) {
bias = 1 - bias;
}
}
catch (NumberFormatException e) {
}
}
boolean shift = toComponentsTo != null;
if (destType == DrawConnection.DEST_GUIDELINE) { // connections to guidelines are always Opposite
connect = ourOppositeDirection[i];
}
AnchorTarget anchorTarget = AnchorTarget.findAnchorTarget(child, ourAnchorTypes[i]);
boolean onDelete = anchorTarget != null && anchorTarget.canDisconnect() && anchorTarget.isMouseHovered();
changeStart = connectStatus.getTime(i);
int previousMode = connectStatus.getPreviousMode(i);
int currentMode =
connectStatus.getCurrentMode(i, selectedConnection, fade, constraintSelected, anyViewSelected, hoverConnection, onDelete);
int x1 = getX(source_rect, i);
int x2 = getX(dest_rect, connect);
int y1 = getY(source_rect, i);
int y2 = getY(dest_rect, connect);
boolean overlap = (i != connect
&& ((isLeftRight[i] && Math.abs(x1 - x2) < 4 && Math.abs(y1 - y2) < dest_rect.height / 2)
|| (!isLeftRight[i] && Math.abs(y1 - y2) < 4 && Math.abs(x1 - x2) < dest_rect.width / 2)));
if (overlap) {
connectType = DrawConnection.TYPE_ADJACENT;
}
if (!ConstraintLayoutHandler.getVisualProperty(ConstraintLayoutHandler.SHOW_MARGINS_PREF_KEY)) {
margin = 0;
marginDistance = 0;
}
DrawConnection
.buildDisplayList(list, new SecondarySelector(child.getNlComponent(), SecondarySelector.Constraint.values()[i]), connectType,
source_rect, i, dest_rect, connect, destType, shift, margin, marginDistance,
isMarginReference, bias, previousMode, currentMode, changeStart);
if (((anchorTarget != null && anchorTarget.isMouseHovered()) || hoverConnection) && viewSelected) {
// When hovering the target or connection of a selected view, draw an animated frame around the target view.
if (destType == DrawConnection.DEST_GUIDELINE) {
int over_size_line = 3000;
dest_rect.grow((connect < 2) ? 1 : over_size_line, (connect < 2) ? over_size_line : 1);
}
boolean drawFrameAsDelete = (currentMode & DrawConnection.HOVER_MASK) == DrawConnection.MODE_DELETING;
DrawAnimatedFrame.add(list, dest_rect, connect, drawFrameAsDelete);
}
}
}
SceneComponent baseLineConnection = (SceneComponent)child.myCache.get("BASELINE");
if (baseLineConnection != null) {
baseLineConnection.fillDrawRect(time, dest_rect); // get the destination rectangle
convert(sceneContext, dest_rect); // scale to screen space
int dest_offset = sceneContext.getSwingDimensionDip(baseLineConnection.getBaseline());
int source_offset = sceneContext.getSwingDimensionDip(child.getBaseline());
source_rect.y += source_offset;
source_rect.height = 0;
dest_rect.y += dest_offset;
dest_rect.height = 0;
changeStart = connectStatus.getTime(ConnectionStatus.DIRECTION_BASELINE);
AnchorTarget anchorTarget = AnchorTarget.findAnchorTarget(child, AnchorTarget.Type.BASELINE);
boolean onDelete = anchorTarget != null && anchorTarget.canDisconnect() && anchorTarget.isMouseHovered();
int previousMode = connectStatus.getPreviousMode(4);
boolean selectedConnection = selectedDirection == SecondarySelector.Constraint.BASELINE.ordinal() && viewSelected;
boolean hoverConnection = hover == SecondarySelector.Constraint.BASELINE.ordinal();
int currentMode =
connectStatus.getCurrentMode(4, selectedConnection, fade, constraintSelected, anyViewSelected, hoverConnection, onDelete);
DrawConnection
.buildDisplayList(list,
new SecondarySelector(child.getNlComponent(), SecondarySelector.Constraint.BASELINE),
DrawConnection.TYPE_BASELINE, source_rect,
DrawConnection.TYPE_BASELINE, dest_rect,
DrawConnection.TYPE_BASELINE,
DrawConnection.DEST_NORMAL,
false, 0, 0, false,
0f, previousMode, currentMode, changeStart);
}
}