Adicionar um campo de plug-in ao Block Factory

As ferramentas para desenvolvedores do Blockly permitem criar blocos personalizados usando blocos. Ele tem suporte para campos publicados como plug-ins, além dos campos que vêm com o Blockly principal. Se você criou um campo personalizado, siga este guia para adicionar suporte a ele na Block Factory. O campo personalizado precisa ser publicado no npm antes de você adicionar suporte a ele. Você também precisa se comprometer a atualizar o campo para acompanhar as mudanças no Blockly. Caso contrário, talvez seja necessário removê-lo da Fábrica de blocos no futuro.

Desenvolvimento na Block Factory

O código-fonte da Block Factory está no repositório blockly-samples no diretório examples/developer-tools.

Para enviar uma mudança nas ferramentas para desenvolvedores em blockly-samples, siga as etapas típicas de desenvolvimento em blockly-samples. Ao contrário do trabalho com plug-ins, você precisa executar npm install diretamente do diretório examples/developer-tools, em vez de no nível raiz de blockly-samples.

Instalar o plug-in

Para que a Block Factory mostre seu campo personalizado na prévia, é necessário instalar o campo. Adicione seu campo como uma dependência npm de developer-tools. Em seguida, registre ou faça qualquer outro trabalho de configuração necessário em developer-tools/src/blocks/index.ts.

Criar um bloco para o campo

Como a Block Factory usa blocos para criar blocos personalizados, você precisa de um bloco que represente seu campo personalizado.

Criar a definição do bloco

Você precisa criar o bloco para seu campo. Se quiser ir além, crie-o usando a Block Factory. O bloco precisa permitir que o usuário configure a configuração exigida pelo campo, como valores padrão e um nome. Adicione essa definição de bloco a developer-tools/src/blocks/fields.ts e importe-a em developer-tools/src/blocks/index.ts.

Adicionar bloco à caixa de ferramentas

Em seguida, adicione esse bloco à definição da caixa de ferramentas para que ele fique acessível aos usuários. A definição da caixa de ferramentas está localizada em developer-tools/src/toolbox.ts. Seu bloco será adicionado à categoria "Campos".

Geradores de código

A Block Factory usa o sistema Code Generator que você já conhece do Blockly. Cada bloco tem um gerador de código para cada tipo de saída gerada pela Block Factory, e os blocos principais montam o código dos blocos filhos na saída correta. Para adicionar suporte a um campo personalizado, é necessário adicionar funções geradoras de código de bloco para cada uma das classes do gerador de código.

Crie um arquivo para o bloco de campo no diretório output-generators/fields. Você vai adicionar os geradores de código de bloco para cada um dos seguintes geradores a esse arquivo. Importe esse arquivo no arquivo blocks/index.ts para que as funções do gerador de código de bloco sejam carregadas no aplicativo.

Definição de JavaScript

O javascriptDefinitionGenerator cria o código que será incluído na definição JavaScript de um bloco que inclui seu campo personalizado. Normalmente, isso significa que o gerador de código em bloco precisa retornar uma linha de código semelhante a .appendField(new YourFieldConstructor(arg1, arg2), 'userSpecifiedName'). Observe que essa linha de código não inclui um ponto e vírgula, porque uma entrada que contém vários campos terá várias chamadas para appendField encadeadas. Os argumentos no construtor são extraídos dos valores que o usuário definiu no bloco de campo. Confira um exemplo desse gerador de código de bloco para FieldAngle:

javascriptDefinitionGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: JavascriptDefinitionGenerator,
): string {
  const name = generator.quote_(block.getFieldValue('FIELDNAME'));
  const angle = block.getFieldValue('ANGLE');
  return `.appendField(new FieldAngle(${angle}), ${name})`;
};

O bloco de ângulo que o usuário arrastou da categoria "Campos" da caixa de ferramentas da Block Factory tem dois campos:

  • FIELDNAME: o usuário pode definir o nome do campo no bloco personalizado
  • ANGLE: o usuário pode definir o valor de ângulo padrão.

Neste gerador de código em blocos, recebemos o valor de ângulo padrão e o transmitimos como o único argumento para o construtor FieldAngle. O nome do campo é sempre transmitido como o segundo argumento para appendField.

Definição de JSON

O jsonDefinitionGenerator é semelhante, mas gera a parte da definição do bloco JSON que corresponde ao seu campo. Normalmente, esse código é um objeto JSON que inclui:

  • type: corresponde ao nome do campo no registro de campos do Blockly.
  • name: o usuário pode definir o nome do campo no bloco personalizado
  • qualquer outra propriedade personalizada necessária pelo método de inicialização JSON do campo.

Confira outro exemplo de FieldAngle:

jsonDefinitionGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: JsonDefinitionGenerator,
): string {
  const code = {
    type: 'field_angle',
    name: block.getFieldValue('FIELDNAME'),
    angle: block.getFieldValue('ANGLE'),
  };
  return JSON.stringify(code);
};

Cabeçalhos de código

O gerador de cabeçalho de código cria a saída de cabeçalhos de código mostrada na Block Factory. Essa saída pode ser alternada entre importações de esmodule e tags de script, dependendo de como o usuário quer carregar o código. Portanto, há duas instâncias de gerador diferentes: uma para cada caso. Você precisa adicionar um gerador de código de bloco para cada um deles. Confira um exemplo para FieldAngle:

importHeaderGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: CodeHeaderGenerator,
): string {
  generator.addHeaderLine(
    `import {registerFieldAngle, FieldAngle} from '@blockly/field-angle';`,
  );
  generator.addHeaderLine(`registerFieldAngle();`);
  return '';
};

scriptHeaderGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: CodeHeaderGenerator,
): string {
  generator.addHeaderLine(
    `<script src="https://unpkg.com/@blockly/field-angle"></script>`,
  );
  generator.addHeaderLine(`registerFieldAngle();`);
  return '';
};

Esses geradores têm um método chamado addHeaderLine que permite especificar uma linha de código que deve ser chamada antes que o campo seja usado no código. Normalmente, isso inclui trabalho como importar o campo ou carregá-lo por uma tag de script e talvez chamar uma função que registre o campo no registro de campos do Blockly.

Para esses dois geradores de código em bloco, todo o código precisa ser adicionado por chamadas para addHeaderLine. Essa função garante que cada linha de cabeçalho seja mostrada apenas uma vez, mesmo que o bloco de campo personalizado seja usado várias vezes em um bloco personalizado. O gerador de código de bloco precisa retornar a string vazia.

Stub do gerador

Por fim, temos o gerador que cria o stub do gerador para o campo. Neste gerador de código em blocos, você vai escrever um código que gera um código que ajuda o usuário a escrever um código que gera um código. Ainda não entendeu? É mais fácil do que parece!

O stub do gerador para um bloco personalizado inclui uma variável predefinida que representa todos os campos do bloco. Em seguida, há um TODO que o usuário precisa concluir para reunir todas essas variáveis na string de código final que o bloco personalizado vai retornar. Isso significa que, normalmente, tudo o que o gerador de código em blocos precisa fazer é retornar a linha que cria essa variável personalizada. Digamos que o usuário esteja criando um bloco personalizado que vai adicionar raios de sol à tela. Eles adicionam um campo de ângulo ao bloco e o chamam de "SUN_DIRECTION". O stub do gerador para esse bloco incluiria a linha const angle_sun_direction = block.getFieldValue("SUN_DIRECTION");. Essa é a linha de código que nosso gerador de código de bloco para o campo de ângulo precisa retornar:

generatorStubGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: GeneratorStubGenerator,
): string {
  const name = block.getFieldValue('FIELDNAME');
  const fieldVar = generator.createVariableName('angle', name);
  return `const ${fieldVar} = block.getFieldValue(${generator.quote_(
    name,
  )});\n`;
};

Para receber um nome padronizado para a variável, chame generator.createVariableName e transmita o tipo do campo (como angle, number etc.) junto com o nome que o usuário deu ao campo.

Realizar o teste

Depois de escrever todas essas partes, você poderá iniciar a Block Factory executando npm start no diretório blockly-samples/examples/developer-tools. Arraste o bloco da categoria de campo, adicione-o a uma entrada em um bloco e observe a mudança na saída. Verifique se a prévia do bloco está correta e se o código de cada uma das seções de saída também está.