연결 형태

연결 모양을 맞춤설정하는 방법에는 여러 가지가 있으며 각 방법마다 어려움이 증가합니다. 이러한 모든 작업에 맞춤 렌더기를 만들어야 합니다.

기본 크기

서로 다른 크기의 연결

기본 도형은 동일하게 유지하면서 너비 또는 높이를 변경하여 연결을 맞춤설정할 수 있습니다. 이렇게 하려면 맞춤 상수 제공자 구성요소를 만들고 일부 상수를 재정의해야 합니다.

렌더기마다 서로 다른 상수를 정의하고 사용하므로 슈퍼 클래스의 참조 문서를 확인하세요.

기본 렌더기의 경우 다음 연결과 이전 연결의 경우 NOTCH_WIDTHNOTCH_HEIGHT를 재정의하고 입력 및 출력 연결의 경우 TAB_WIDTHTAB_HEIGHT을 재정의할 수 있습니다.

class CustomConstantProvider extends Blockly.blockRendering.ConstantProvider {
  constructor() {
    super();
    this.NOTCH_WIDTH = 20;
    this.NOTCH_HEIGHT = 10;
    this.TAB_HEIGHT = 8;
  }
}

기본 도형

다른 형태의 연결부

기본 모양을 재정의하여 연결을 맞춤설정할 수 있습니다. 기본 도형에는 높이, 너비, 두 개의 경로가 있습니다.

각 경로는 같은 형태를 그리지만 반대쪽 끝에서 그려집니다.

양방향에서 그려진 홈

이렇게 해야 하는 이유는 창이 블록의 윤곽선을 그릴 때 각 종류의 연결을 양방향으로 그리기 때문입니다. 예를 들어 이전 연결은 왼쪽에서 오른쪽으로 그려지지만 다음 연결은 오른쪽에서 왼쪽으로 그려집니다. 따라서 두 경우 모두에 대한 경로를 제공해야 합니다.

블록이 그려지는 방향

다음 및 이전 연결의 경우 makeNotch 메서드를 재정의하고 입력 및 출력 연결의 경우 makePuzzleTab 메서드를 재정의할 수 있습니다.

class CustomConstantProvider extends Blockly.blockRendering.ConstantProvider {
  makePuzzleTab() {
    const width = this.TAB_WIDTH;
    const height = this.TAB_HEIGHT;
    return {
      type: this.SHAPES.PUZZLE,
      width,
      height,
      pathUp: Blockly.utils.svgPaths.line([
          Blockly.utils.svgPaths.point(-width, -height / 2),
          Blockly.utils.svgPaths.point(width, -height / 2)]),
      pathDown: Blockly.utils.svgPaths.line([
          Blockly.utils.svgPaths.point(-width, height / 2),
          Blockly.utils.svgPaths.point(width, height / 2)]),
    };
  }
}

경로 문자열을 정의하는 방법에 관한 자세한 내용은 MDN SVG 경로 문서를 참고하세요. Blockly.utils.svgPaths 네임스페이스는 이러한 문자열을 더 쉽게 읽을 수 있도록 이러한 문자열을 둘러싸는 씬 래퍼로 제공됩니다.

연결 확인을 위한 셰이프

다른 모양과 연결부

연결의 연결 확인에 따라 형태를 변경하여 연결을 맞춤설정할 수 있습니다.

이를 통해 다양한 데이터 유형을 나타내는 여러 도형을 만들 수 있습니다. 예를 들어 문자열은 삼각형 연결로, 불리언은 둥근 연결로 표시할 수 있습니다.

여러 연결 확인에 다른 도형을 제공하려면 shapeFor 메서드를 재정의해야 합니다. 반환된 도형은 init에서 초기화되어야 합니다.

지원되는 도형의 종류에 대한 자세한 내용은 기본 도형을 참고하세요.

export class ConstantProvider extends Blockly.blockRendering.BaseConstantProvider {
  shapeFor(connection) {
    let check = connection.getCheck();
    // For connections with no check, match any child block.
    if (!check && connection.targetConnection) {
      check = connection.targetConnection.getCheck();
    }

    if (check && check.includes('String')) return this.TRIANGULAR_TAB;
    if (check && check.includes('Boolean')) return this.ROUND_TAB;

    return super.shapeFor(connection);
  }
}

맞춤 입력

완전한 맞춤 입력을 만들어 연결 형태를 맞춤설정할 수 있습니다. 이 작업은 일부 연결을 다른 연결과 다르게 표시하고 연결 확인을 기반으로 하지 않으려는 경우에만 실행됩니다.

예를 들어 일부 값 입력을 명령문 입력처럼 들여쓰기 하려면 커스텀 입력을 만들면 됩니다.

맞춤 입력 클래스 만들기

맞춤 입력값 만들기 단계를 따릅니다.

측정 가능한 이벤트 만들기

커스텀 입력을 나타내는 측정 가능 객체를 만들어야 합니다.

맞춤 입력 측정 가능 항목은 Blockly.blockRendering.InputConnection에서 상속해야 합니다. 또한 입력의 모양을 그리는 데 필요한 추가 측정 데이터가 포함될 수도 있습니다.

export class CustomInputMeasurable extends Blockly.blockRendering.InputConnection {
  constructor(constants, input) {
    super(constants, input);

    // Any extra measurement data...
  }
}

측정 가능한 항목을 인스턴스화합니다.

렌더링 정보는 맞춤 측정 가능 항목을 인스턴스화해야 합니다. 이렇게 하려면 addInput_ 메서드를 재정의해야 합니다.

export class RenderInfo extends Blockly.blockRendering.RenderInfo {
  addInput_(input, activeRow) {
    if (input instanceof CustomInput) {
      activeRow.elements.push(new CustomInputMeasurable(this.constants_, input));
    }
    super.addInput_(input, activeRow);
  }
}

원하는 경우 행을 만듭니다.

기본적으로 입력을 입력해도 새 이 생성되지 않습니다. 입력으로 행의 끝을 트리거하려면 렌더링 정보shouldStartNewRow_ 메서드를 재정의해야 합니다.

export class RenderInfo extends Blockly.blockRendering.RenderInfo {
  shouldStartNewRow_(currInput, prevInput) {
    if (prevInput instanceof CustomInput) return true;
    return super.shouldStartNewRow_(currInput, prevInput);
  }
}

원하는 경우 입력에 사용할 도형 만들기

노치 및 퍼즐 탭과 마찬가지로 입력 모양을 상수로 저장하는 것이 좋습니다. 이렇게 하면 코드를 체계적으로 유지하고 나중에 쉽게 수정할 수 있습니다.

입력 그리기

마지막으로 도형을 그리려면 을 수정해야 합니다.

맞춤 입력은 다음 중 하나를 사용할 수 있습니다.

  • 명세서 입력처럼 블록의 윤곽선에 영향을 줌

    개요 입력 이미지

  • 또는 인라인 값 입력과 같은 블록 내부에 영향을 줍니다.

    내부 입력 이미지

입력이 블록의 윤곽선에 영향을 주면 drawOutline_를 재정의하고 그렇지 않으면 drawInternals_를 재정의합니다.

export class Drawer extends Blockly.blockRendering.Drawer {
  drawOutline_() {
    this.drawTop_();
    for (let r = 1; r < this.info_.rows.length - 1; r++) {
      const row = this.info_.rows[r];

      // Insert checks for your input here!
      if (row.getLastInput() instanceof CustomInputMeasurable) {
        this.drawCustomInput(row);
      } else if (row.hasJaggedEdge) {
        this.drawJaggedEdge_(row);
      } else if (row.hasStatement) {
        this.drawStatementInput_(row);
      } else if (row.hasExternalInput) {
        this.drawValueInput_(row);
      } else {
        this.drawRightSideRow_(row);
      }
    }
    this.drawBottom_();
    this.drawLeft_();
  }

  protected drawInternals_() {
    for (const row of rows) {
      for (const elem of row) {

        // Insert checks for your input here!
        if (elem instanceof CustomInputMeasurable) {
          this.drawCustomInput(elem);
        }

        if (Types.isInlineInput(elem)) {
          this.drawInlineInput_(elem as InlineInput);
        } else if (Types.isIcon(elem) || Types.isField(elem)) {
          this.layoutField_(elem as Field | Icon);
        }
      }
    }
  }
}