Extensões e mutadores

Extensões são funções executadas em cada bloco de um determinado tipo, à medida que o bloco criados. Eles geralmente adicionam uma configuração ou comportamento personalizado a um bloco.

Um mutador é um tipo especial de extensão que adiciona serialização personalizada. às vezes interface, para um bloco.

Extensões

Extensões são funções executadas em cada bloco de um determinado tipo, à medida que o bloco criados. Ele pode adicionar uma configuração personalizada (por exemplo, definir a dica do bloco) ou comportamento personalizado (por exemplo, adicionar uma escuta de evento ao bloco).

// This extension sets the block's tooltip to be a function which displays
// the parent block's tooltip (if it exists).
Blockly.Extensions.register(
    'parent_tooltip_extension',
    function() { // this refers to the block that the extension is being run on
      var thisBlock = this;
      this.setTooltip(function() {
        var parent = thisBlock.getParent();
        return (parent && parent.getInputsInline() && parent.tooltip) ||
            Blockly.Msg.MATH_NUMBER_TOOLTIP;
      });
    });

As extensões precisam ser "registradas" para que possam ser associados a uma string de dados. Em seguida, você pode atribuir essa chave de string à propriedade extensions do do tipo de bloco JSON definição para aplicar a extensão ao bloco.

{
 //...,
 "extensions": ["parent_tooltip_extension",]
}

Você também pode adicionar várias extensões de uma vez. Observe que o extensions deve ser uma matriz, mesmo que você esteja aplicando apenas uma extensão.

{
  //...,
  "extensions": ["parent_tooltip_extension", "break_warning_extension"],
}

Mixins

O Blockly também oferece um método conveniente para situações em que você deseja adicionar algumas propriedades/funções auxiliares em um bloco, mas não executá-las imediatamente. Isso permite que você registre um mixin Objeto que contém todas as propriedades/métodos adicionais. O objeto mixin é então envolvido em uma função que aplica a mistura sempre que uma instância de o tipo de bloco fornecido é criado.

Blockly.Extensions.registerMixin('my_mixin', {
  someProperty: 'a cool value',

  someMethod: function() {
    // Do something cool!
  }
))`

Chaves de string associadas a mixins podem ser referenciadas em JSON, assim como qualquer outra .

{
 //...,
 "extensions": ["my_mixin"],
}

Mutadores

Um mutador é um tipo especial de extensão que adiciona serialização adicional que é salvo e carregado) em um bloco. Por exemplo, o gerenciador Os blocos controls_if e list_create_with precisam de serialização extra para que elas podem economizar a quantidade de entradas que têm.

Alterar a forma do bloco não significa necessariamente que você precise com serialização extra. Por exemplo, o bloco math_number_property muda forma, mas faz isso com base em um campo suspenso, cujo valor já é serializados. Assim, ele pode usar apenas um campo validador e não precisa de um mutador.

Consulte a serialização página para mais informações sobre quando você precisa e quando não precisa de um mutador.

Os mutadores também oferecem uma IU integrada para que os usuários mudem as formas dos blocos se você fornece alguns métodos opcionais.

Hooks de serialização

Os mutadores têm dois pares de hooks de serialização com os quais trabalham. Um par de ganchos funciona com o novo sistema de serialização JSON, e o outro par funciona com a antigo sistema de serialização XML. Você precisa fornecer pelo menos um desses pares.

saveExtraState e loadExtraState

saveExtraState e loadExtraState são hooks de serialização que funcionam com a novo sistema de serialização JSON. saveExtraState retorna um JSON serializável um valor que representa o estado extra do bloco, e loadExtraState aceita esse mesmo valor serializável do JSON e o aplica ao bloco.

// These are the serialization hooks for the lists_create_with block.
saveExtraState: function() {
  return {
    'itemCount': this.itemCount_,
  };
},

loadExtraState: function(state) {
  this.itemCount_ = state['itemCount'];
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

O JSON resultante será semelhante a este:

{
  "type": "lists_create_with",
  "extraState": {
    "itemCount": 3 // or whatever the count is
  }
}
Sem estado

Se o bloco estiver no estado padrão quando for serializado, então seu O método saveExtraState pode retornar null para indicar isso. Se as O método saveExtraState retorna null, e nenhuma propriedade extraState é adicionada a no JSON. Isso mantém o tamanho do arquivo salvo pequeno.

Serialização completa e dados de backup

saveExtraState também recebe um parâmetro doFullSerialization opcional. Isso é usado por blocos que fazem referência ao estado serializado por uma serializer (como modelos de dados de apoio). O parâmetro indica que o estado referenciado não estará disponível quando o bloco for desserializado, então o deve serializar todo o estado de apoio. Por exemplo, verdadeiro quando um bloco individual é serializado ou quando um bloco é copiado e colado.

Dois casos de uso comuns para isso são:

  • Quando um bloco individual é carregado em um espaço de trabalho em que os dados de apoio não existe, ele tem informações suficientes em seu próprio estado para criar um novo modelo de dados.
  • Quando um bloco é copiado e colado, ele sempre cria um novo apoio modelo de dados em vez de referenciar um que já existe.

Alguns blocos que usam isso são Blocos @blockly/block-shareable-procedures. Normalmente eles serializam uma referência a um modelo de dados de apoio, que armazena seu estado. No entanto, se o parâmetro doFullSerialization for verdadeiro, eles serializam todos os o estado delas. Os blocos de procedimento compartilhável usam isso para garantir que, quando são copiados e colados eles criam um novo modelo de dados de apoio, em vez de referenciar um modelo atual.

mutateToDom e domToMutation

mutationToDom e domToMutation são hooks de serialização que funcionam com a antigo sistema de serialização XML. Use esses ganchos apenas se precisar (por exemplo, trabalhando em uma base de código antiga que ainda não foi migrada), use saveExtraState e loadExtraState.

mutationToDom retorna um nó XML que representa o estado extra da bloco, e domToMutation aceita o mesmo nó XML e aplica o estado a do bloco.

// These are the old XML serialization hooks for the lists_create_with block.
mutationToDom: function() {
  // You *must* create a <mutation></mutation> element.
  // This element can have children.
  var container = Blockly.utils.xml.createElement('mutation');
  container.setAttribute('items', this.itemCount_);
  return container;
},

domToMutation: function(xmlElement) {
  this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

O XML resultante será semelhante a este:

<block type="lists_create_with">
  <mutation items="3"></mutation>
</block>

Se a função mutationToDom retornar nulo, nenhum elemento extra será adicionados ao XML.

Ganchos da interface

Se você fornecer certas funções como parte do mutador, o Blockly adicionará um "mutator" padrão interface ao seu bloco.

Você não precisa usar essa interface se quiser adicionar serialização extra. Você pode usar uma interface personalizada, como o bloco blocos-mais-menos plug-in fornece, ou você pode não usar nenhuma interface.

compor e decompor

A interface padrão depende das funções compose e decompose.

decompose "explode" o bloco em subblocos menores que podem ser movidos adicionados e excluídos. Essa função deve retornar um "bloco superior" que é no bloco principal no espaço de trabalho do mutator ao qual os subblocos se conectam.

Em seguida, compose interpreta a configuração dos subblocos e os usa para modificar o bloco principal. Essa função precisa aceitar o bloco superior que era retornados por decompose como um parâmetro.

Essas funções são "misturadas" ao bloco sendo "mutado" então this pode ser usada para se referir a esse bloco.

// These are the decompose and compose functions for the lists_create_with block.
decompose: function(workspace) {
  // This is a special sub-block that only gets created in the mutator UI.
  // It acts as our "top block"
  var topBlock = workspace.newBlock('lists_create_with_container');
  topBlock.initSvg();

  // Then we add one sub-block for each item in the list.
  var connection = topBlock.getInput('STACK').connection;
  for (var i = 0; i < this.itemCount_; i++) {
    var itemBlock = workspace.newBlock('lists_create_with_item');
    itemBlock.initSvg();
    connection.connect(itemBlock.previousConnection);
    connection = itemBlock.nextConnection;
  }

  // And finally we have to return the top-block.
  return topBlock;
},

// The container block is the top-block returned by decompose.
compose: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we collect up all of the connections of on our main block that are
  // referenced by our sub-blocks.
  // This relates to the saveConnections hook (explained below).
  var connections = [];
  while (itemBlock && !itemBlock.isInsertionMarker()) {  // Ignore insertion markers!
    connections.push(itemBlock.valueConnection_);
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }

  // Then we disconnect any children where the sub-block associated with that
  // child has been deleted/removed from the stack.
  for (var i = 0; i < this.itemCount_; i++) {
    var connection = this.getInput('ADD' + i).connection.targetConnection;
    if (connection && connections.indexOf(connection) == -1) {
      connection.disconnect();
    }
  }

  // Then we update the shape of our block (removing or adding iputs as necessary).
  // `this` refers to the main block.
  this.itemCount_ = connections.length;
  this.updateShape_();

  // And finally we reconnect any child blocks.
  for (var i = 0; i < this.itemCount_; i++) {
    connections[i].reconnect(this, 'ADD' + i);
  }
},

saveConnections

Também é possível definir uma função saveConnections que funcione com à interface padrão. Essa função dá a você a chance de associar filhos de bloco principal (que existe no espaço de trabalho principal) com subblocos que existem no seu espaço de trabalho mutador. Você pode usar esses dados para garantir que o compose funciona reconecta adequadamente os filhos do bloco principal quando seus sub-blocos são reorganizados.

saveConnections precisa aceitar o "bloco superior" retornado por decompose como parâmetro. Se a função saveConnections estiver definida, o Blockly o chamará antes de chamar compose.

saveConnections: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we go through and assign references to connections on our main block
  // (input.connection.targetConnection) to properties on our sub blocks
  // (itemBlock.valueConnection_).
  var i = 0;
  while (itemBlock) {
    // `this` refers to the main block (which is being "mutated").
    var input = this.getInput('ADD' + i);
    // This is the important line of this function!
    itemBlock.valueConnection_ = input && input.connection.targetConnection;
    i++;
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }
},

Registrando

Os mutadores são apenas um tipo especial de extensão, então eles também precisam ser registradas antes de serem usadas no JSON do tipo de bloco definição.

// Function signature.
Blockly.Extensions.registerMutator(name, mixinObj, opt_helperFn, opt_blockList);

// Example call.
Blockly.Extensions.registerMutator(
    'controls_if_mutator',
    { /* mutator methods */ },
    undefined,
    ['controls_if_elseif', 'controls_if_else']);
  • name: uma string a ser associada ao mutador para que você possa usá-lo em JSON.
  • mixinObj: um objeto que contém os vários métodos de mutação. Por exemplo: saveExtraState e loadExtraState.
  • opt_helperFn: uma função auxiliar opcional que será ser executada no bloco depois que o mixin for misturado.
  • opt_blockList: uma matriz opcional de tipos de bloco (como strings) que serão adicionado ao menu suspenso na interface do mutator padrão, se os métodos da interface também estiverem definido.

Ao contrário das extensões, cada tipo de bloqueio só pode ter um mutador.

{
  //...
  "mutator": "controls_if_mutator"
}

Função auxiliar

Junto com o mixin, um mutador pode registrar uma função auxiliar. Essa função é executada em cada bloco do tipo especificado depois que ele é criado e o mixinObj é adicionados. Ela pode ser usada para adicionar outros gatilhos ou efeitos a uma mutação.

Por exemplo, você pode adicionar um assistente ao seu bloco tipo lista que define o Número inicial de itens:

var helper = function() {
  this.itemCount_ = 5;
  this.updateShape();
}