Modificar definições de bloco

Uma pergunta comum é como modificar a definição de um bloco. Por exemplo, você pode adicionar uma verificação de conexão ou mudar um campo para uma entrada de valor.

Em geral, não é possível modificar uma definição de bloco no lugar.

Como modificar uma definição

Se você quiser modificar a definição de um tipo de bloco existente:

  1. Faça uma cópia da definição atual, incluindo os geradores de código de bloco.
  2. Modifique a cópia e dê um novo nome ao tipo.
  3. Adicione a nova definição a Blockly.Blocks.

Por que não posso modificar diretamente uma definição?

Se você quer saber por que não é possível modificar uma definição, continue lendo. Vamos considerar algumas possibilidades.

Modificar uma definição diretamente

Há duas maneiras de modificar diretamente a definição de um bloco existente: monkeypatching e bifurcação do código. Não recomendamos nenhum dos dois, porque você corre o risco de quebrar o código que depende do código com monkeypatch ou bifurcado. Ambas as técnicas também dificultam a integração de atualizações e correções de bugs. Para mais informações, consulte E se monkeypatching? e Fork Blockly.

Criar uma subclasse de uma definição

Talvez você queira subclassificar uma definição existente. Infelizmente, isso não é possível porque as definições de bloco não são classes, são mixins. Por exemplo, não há como substituir uma propriedade de cor porque a definição não tem uma propriedade de cor. Em vez disso, ele tem uma função init que chama setColour para definir a propriedade de cor no bloco. Como a chamada está dentro de init, não há como substituí-la sem substituir toda a função init.

Substituir uma função em uma definição

É possível substituir uma função em uma definição:

Blockly.Blocks['existing_block'].init = function() {/*new function*/};

Isso funciona, mas você precisa copiar e modificar a função existente. Não é possível substituir magicamente apenas uma linha. Isso tem vários problemas:

  • Não é muito diferente de copiar e modificar toda a definição.
  • Não é possível usá-lo para modificar a função init de blocos definidos em JSON, como os blocos integrados do Blockly. Isso ocorre porque não há uma função init para copiar. Ela é gerada no momento da execução.
  • Ele exige que você defina o bloco usando JavaScript, o que pode causar problemas com a localização.

Substituir os resultados do init

Uma maneira de "substituir apenas uma linha" de uma função init é substituir a função init por uma que chame a função init original e, em seguida, substitua o resultado dessa chamada. Por exemplo, o código a seguir muda a cor do bloco logic_null:

const originalInit = Blockly.Blocks['logic_null'].init;
Blockly.Blocks['logic_null'].init = function() {
  originalInit.call(this);
  this.setColour(300);
}

Infelizmente, isso é menos útil do que parece. Exemplo:

  • Ampliar as verificações de conexão ou aplicar um validador de campo menos restritivo pode invalidar as suposições feitas por geradores de código de bloco e manipuladores de eventos.

  • A substituição de um campo por uma entrada de valor vai interromper os geradores de código de bloco e os validadores de campo e pode interromper os manipuladores de eventos. Também pode ser extremamente difícil fazer isso para blocos localizados, porque localidades diferentes podem resultar em blocos com tipos e ordens diferentes de entradas e campos.

Substituir um par de chave-valor em uma definição JSON

Se o JSON de um bloco estiver disponível publicamente, talvez seja possível substituir valores JSON individuais. Exemplo:

// Block definition.
blockJson = {...};
Blockly.Blocks['my_block'] = {
  init: function() {
    initJson(blockJson); // Called when the block is created.
  }
}

// Third-party code.
blockJson.colour = 100;

No entanto, isso só funciona se o objeto JSON estiver disponível publicamente, a definição definir explicitamente a função init e a função init chamar initJson. Ele não funciona se o JSON for transmitido para defineBlocksWithJsonArray ou createBlockDefinitionsFromJsonArray porque o JSON é processado antes que um terceiro tenha a chance de modificá-lo. Os blocos integrados do Blockly usam createBlockDefinitionsFromJsonArray.

Mas e se o JSON não estiver definido dessa maneira? Não seria possível substituir as propriedades JSON? Não. A definição contém uma função init (não JSON) e não há uma função para converter uma função init em JSON.

// Doesn't work. There is no getJson() function.
const json = Blockly.Blocks['existing_block'].getJson();
json['message0'] = 'my new message0';
Blockly.Blocks['existing_block'].init = function () {
  initJson(json);
};

Design para reutilização

Ao projetar seus próprios blocos personalizados, você pode projetá-los de maneiras que promovam a reutilização.

Reutilizar JSON

Se você tiver dois blocos substancialmente semelhantes, crie uma definição JSON mãe e reutilize-a nas definições filhas. Exemplo:

const parentJson = {
  // shared properties
};

Blockly.Blocks['child_block_1'] = {
  init: function() {
    initJson({...parentJson, colour: 100})
  }
}

Blockly.Blocks['child_block_2'] = {
  init: function() {
    initJson({...parentJson, colour: 200})
  }
}

Outra alternativa é definir o JSON em um objeto disponível publicamente e transmitir esse objeto para initJson na função init. Isso permite que outras pessoas substituam propriedades individuais. Para mais informações, consulte Substituir um par de chave-valor em uma definição JSON.

Reutilizar funções

Os blocos podem definir várias funções padrão, como manipuladores de eventos no nível do bloco, dicas de ferramentas personalizadas, validadores de campo e as funções usadas por modificadores, além de funções que fornecem comportamento personalizado, como uma função que define valores de campo a partir de dados externos, como a posição atual do braço de um robô.

Talvez seja possível reutilizar essas funções em blocos.

Usar um campo de lista suspensa

Se você tiver um conjunto de blocos substancialmente iguais, exceto por um operador, poderá projetar um único bloco com um campo suspenso para o operador. Exemplo:

  • O bloco logic_operation integrado usa um menu suspenso com os operadores and e or.
  • O bloco math_arithmetic integrado usa um menu suspenso com os operadores +, -, ×, ÷ e ^.

Criar geradores de código para esses blocos geralmente é um pouco mais complexo, mas ainda mais fácil do que escrever e manter vários blocos.

Usar um mutador

Se você tiver um conjunto de blocos que representem variações diferentes da mesma estrutura de programação, será possível criar um único bloco que use um mutador. Por exemplo, o bloco controls_if integrado pode representar várias variações de instruções if-then-else.