private static void buildListConnections()

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);
    }
  }