Extensiones y mutadores

Las extensiones son funciones que se ejecutan en cada bloque de un tipo determinado mientras el bloque crear. A menudo, estos agregan una configuración o un comportamiento personalizados a un bloque.

Un mutador es un tipo especial de extensión que agrega serialización personalizada y a veces la IU, a un bloque.

Extensiones

Las extensiones son funciones que se ejecutan en cada bloque de un tipo determinado mientras el bloque crear. Pueden agregar una configuración personalizada (p. ej., configurar el cuadro de información del bloque). comportamiento personalizado (por ejemplo, agregar un objeto de escucha de eventos al bloque).

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

Las extensiones se deben "registrar" para que puedan asociarse con una cadena . Luego, puedes asignar esta clave de cadena a la propiedad extensions de tu JSON de tipo de bloque definición para aplicar la extensión al bloque.

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

También puede agregar varias extensiones a la vez. Ten en cuenta que el elemento extensions La propiedad debe ser un array, incluso si solo aplicas una extensión.

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

Mixes

Blockly también es un método útil para situaciones en las que quieras agregar algunas propiedades o funciones auxiliares a un bloque, pero no ejecutarlas de inmediato. Esta funciona permitiéndote registrar una mezcla que contenga todas las propiedades o los métodos adicionales. El objeto mixin se une a una función que aplica la combinación cada vez que se crea se crea el tipo de bloque determinado.

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

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

Se puede hacer referencia a las claves de cadenas asociadas con combinaciones en JSON como a cualquier otro .

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

Mutadores

Un mutador es un tipo especial de extensión que agrega serialización extra (extra que se guarda y se carga) en un bloque. Por ejemplo, el sistema integrado Los bloques controls_if y list_create_with necesitan serialización adicional para que puede ahorrar cuántas entradas tiene.

Ten en cuenta que cambiar la forma del bloque no necesariamente significa que necesites serialización adicional. Por ejemplo, el bloque math_number_property cambia forma, pero lo hace basándose en un campo desplegable, cuyo valor ya se serializado. Por lo tanto, puede usar un campo validador y no necesitas un mutador.

Consulta el método serialización página de más información sobre cuándo necesitas un mutador y cuándo no.

Los mutadores también proporcionan una IU integrada para que los usuarios cambien las formas de los bloques si proporcionas algunos métodos opcionales.

Hooks de serialización

Los mutadores tienen dos pares de ganchos de serialización con los que trabajan. Un par de ganchos funciona con el nuevo sistema de serialización JSON, y el otro par trabaja con el el antiguo sistema de serialización XML. Debes proporcionar al menos uno de estos pares.

SaveExtraState y loadExtraState

saveExtraState y loadExtraState son hooks de serialización que funcionan con el nuevo sistema de serialización JSON. saveExtraState muestra un archivo JSON serializable valor que representa el estado adicional del bloque, y loadExtraState acepta ese mismo valor JSON serializable y lo aplica al bloque.

// 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_();
},

El JSON resultante se verá de la siguiente manera:

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

Si el bloque se encuentra en el estado predeterminado cuando se serializa, el El método saveExtraState puede mostrar null para indicarlo. Si el El método saveExtraState muestra null; por lo tanto, no se agrega ninguna propiedad extraState. el JSON. De esta manera, el tamaño del archivo para guardar se mantiene pequeño.

Datos de serialización y copia de seguridad completos

saveExtraState también recibe un parámetro doFullSerialization opcional. Esta lo usan bloques que hacen referencia al estado serializado por una dirección serializador (como modelos de datos de copia de seguridad). El parámetro indica que el estado al que se hace referencia no estará disponible cuando se deserialice el bloque. debe serializar todo el estado de copia de seguridad. Por ejemplo, este es true cuando un bloque individual se serializa o cuando un bloque se copia y pega.

Dos casos de uso comunes para esto son los siguientes:

  • Cuando se carga un bloque individual en un espacio de trabajo en el que los datos de copia de seguridad modelo no existe, tiene suficiente información en su propio estado para para crear un modelo de datos nuevo.
  • Cuando se copia y pega un bloque, siempre se crea una copia de seguridad nueva en lugar de hacer referencia a uno existente.

Algunos de los bloques que usan esto son los @blockly/block-shareable-procedures. Normalmente serializan una referencia a un modelo de datos de copia de seguridad, que almacena su estado. Pero si el parámetro doFullSerialization es verdadero, entonces serializan su estado. Los bloques de procedimiento que se pueden compartir usan esto para asegurarse de que cuando se copia y pega, crea un nuevo modelo de datos de copia de seguridad, en lugar de hacer referencia a un para el modelo existente.

mutationToDom y domToMutation

mutationToDom y domToMutation son hooks de serialización que funcionan con el el antiguo sistema de serialización XML. Utiliza estos ganchos solo si debes hacerlo (p.ej., trabajando con una base de código antigua que todavía no se migró), de lo contrario, usa saveExtraState y loadExtraState.

mutationToDom muestra un nodo XML que representa el estado adicional del objeto bloque, y domToMutation acepta ese mismo nodo XML y aplica el estado a el bloque.

// 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_();
},

El XML resultante se verá de la siguiente manera:

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

Si tu función mutationToDom muestra un valor nulo, no se aplicará ningún elemento adicional agregado al XML.

Hooks de IU

Si proporcionas ciertas funciones como parte de tu mutador, Blockly agregará “mutador” predeterminado IU a tu bloque.

No necesitas usar esta IU si quieres agregar serialización adicional. Podrías usar una IU personalizada, como blocks-plus-minus complemento proporciona o puedes no usar ninguna IU.

componer y desglosar

La IU predeterminada se basa en las funciones compose y decompose.

decompose "estalla" el bloque en subbloques más pequeños que se pueden mover alrededor, se agregan y se borran. Esta función debería mostrar un "bloque superior" que es es el bloque principal del lugar de trabajo del mutador al que se conectan los subbloques.

Luego, compose interpreta la configuración de los subbloques y los usa para modificar el bloque principal. Esta función debe aceptar el "bloque superior" que era que muestra decompose como parámetro.

Ten en cuenta que estas funciones se “mezclan” al bloque que está “mutando” así que this para referirse a ese bloque.

// 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

También puedes definir una función saveConnections que se ejecute con la IU predeterminada. Esta función te da la oportunidad de asociar los elementos secundarios de tu el bloque principal (que existe en el espacio de trabajo principal) con los subbloques que existen en en el lugar de trabajo del mutador. Luego, puedes usar estos datos para asegurarte de que tu compose vuelve a conectar correctamente los elementos secundarios de tu bloque principal cuando subbloques están reorganizados.

saveConnections debe aceptar el "bloque superior" devuelto por tu decompose la función como parámetro. Si la función saveConnections está definida, Blockly lo llamará antes de llamar a 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();
  }
},

Registrándose

Los mutadores son solo un tipo especial de extensión, así que también deben se registran antes de que puedas usarlos en el archivo JSON definición.

// 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: Es una cadena para asociar con el mutador de modo que puedas usarla en JSON.
  • mixinObj: Un objeto que contiene los diversos métodos de mutación. P.ej., saveExtraState y loadExtraState.
  • opt_helperFn: Es una función auxiliar opcional que realizará lo siguiente: ejecutar en el bloque después de que se mezcle la mezcla.
  • opt_blockList: Es un array opcional de tipos de bloques (como cadenas) que se agregado al menú flotante en la IU predeterminada del mutador, si los métodos de la IU también definido.

Ten en cuenta que, a diferencia de las extensiones, cada tipo de bloque solo puede tener un mutador.

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

Función auxiliar

Junto con la combinación, un mutador puede registrar una función auxiliar. Esta función es ejecutar en cada bloque del tipo dado una vez creado, y el mixinObj se agregado. Se puede usar para agregar activadores o efectos adicionales a una mutación.

Por ejemplo, puedes agregar un ayudante a tu bloque tipo lista que establezca la cantidad inicial de elementos:

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