Extensions et mutations

Les extensions sont des fonctions qui s'exécutent sur chaque bloc d'un type donné lorsque le bloc est créé. Ils ajoutent souvent une configuration ou un comportement personnalisés à un bloc.

Un mutateur est un type spécial d'extension qui ajoute une sérialisation personnalisée, et parfois à l'UI, à un bloc.

Extensions

Les extensions sont des fonctions qui s'exécutent sur chaque bloc d'un type donné lorsque le bloc est créé. Il peut ajouter une configuration personnalisée (par exemple, en définissant l'info-bulle du bloc) ou comportement personnalisé (par exemple, ajout d'un écouteur d'événements au bloc).

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

Les extensions doivent être enregistrées. afin qu'ils puissent être associés à une chaîne . Vous pouvez ensuite attribuer cette clé de chaîne à la propriété extensions de votre le code JSON du type de bloc définition pour appliquer la l'extension au bloc.

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

Vous pouvez également ajouter plusieurs extensions à la fois. Notez que extensions doit être un tableau, même si vous n'appliquez qu'une seule extension.

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

Mix

Blockly offre également une méthode pratique pour les situations où vous souhaitez ajouter des propriétés/fonctions d'assistance à un bloc, mais pas de les exécuter immédiatement. Ce car elle vous permet d'enregistrer un mixin qui contient toutes vos propriétés/méthodes supplémentaires. Objet mixin est ensuite encapsulé dans une fonction qui applique le mixin chaque fois qu'une instance de le type de bloc donné est créé.

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

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

Les clés de chaîne associées aux mixins peuvent être référencées dans JSON comme n'importe quelle autre .

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

Mutateurs

Un mutateur est un type spécial d'extension qui ajoute une sérialisation supplémentaire enregistré et chargé) dans un bloc. Par exemple, l'API intégrée Les blocs controls_if et list_create_with nécessitent une sérialisation supplémentaire pour que il peut enregistrer le nombre d'entrées dont il dispose.

Notez que la modification de la forme de votre volume ne signifie pas nécessairement que vous devez une sérialisation supplémentaire. Par exemple, le bloc math_number_property change mais en se basant sur un champ de liste déroulante, dont la valeur est déjà sérialisées. Par conséquent, il peut simplement utiliser un champ de l'outil de validation ont besoin d'un mutateur.

Examinez la sérialisation de Google pour plus d'informations sur les cas où vous avez besoin d'un mutateur ou non.

Ils fournissent également une interface utilisateur intégrée permettant aux utilisateurs de modifier la forme des volumes si vous fournissez des méthodes facultatives.

Hooks de sérialisation

Les mutateurs utilisent deux paires de hooks de sérialisation. Une paire de crochets fonctionne avec le nouveau système de sérialisation JSON, tandis que l'autre paire fonctionne avec le de l'ancien système de sérialisation XML. Vous devez fournir au moins l'une de ces paires.

saveExtraState et loadExtraState

saveExtraState et loadExtraState sont des hooks de sérialisation qui fonctionnent avec un nouveau système de sérialisation JSON. saveExtraState renvoie un objet JSON sérialisable qui représente l'état supplémentaire du bloc, et loadExtraState accepte cette même valeur JSON sérialisable et l'applique au bloc.

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

Le fichier JSON obtenu se présente comme suit:

{
  "type": "lists_create_with",
  "extraState": {
    "itemCount": 3 // or whatever the count is
  }
}
Aucun état

Si votre bloc est dans son état par défaut lorsqu'il est sérialisé, votre La méthode saveExtraState peut renvoyer null pour l'indiquer. Si votre La méthode saveExtraState renvoie null, puis aucune propriété extraState n'est ajoutée à au format JSON. Cela permet de réduire la taille du fichier d'enregistrement.

Sérialisation complète et données de sauvegarde

saveExtraState reçoit également un paramètre doFullSerialization facultatif. Ce est utilisée par les blocs qui font référence à l'état sérialisé par une autre sérialiseur (comme les modèles de données de sauvegarde). Le paramètre indique que l'état référencé n'est pas disponible lorsque le bloc est désérialisé. Par conséquent, doit sérialiser tout l'état de support lui-même. Par exemple, il s'agit Valeur true lorsqu'un bloc individuel est sérialisé ou lorsqu'un bloc est copié et collé.

Voici deux cas d'utilisation courants:

  • Lorsqu'un bloc individuel est chargé dans un espace de travail où les données de sauvegarde n'existe pas, il dispose de suffisamment d'informations dans son propre état pour créer un modèle de données.
  • Lorsqu'un bloc est copié-collé, il crée toujours un nouveau support au lieu de référencer un modèle existant.

Certains blocs qui l'utilisent sont Blocages @blockly/block-shareable-procedures Normalement ils sérialisent une référence à un modèle de données de sauvegarde, qui stocke leur état. Mais si le paramètre doFullSerialization est "true", ils sérialisent l'ensemble leur état. Les blocs de procédures partageables l'utilisent pour s'assurer que lorsqu'ils Ils créent un modèle de données de sauvegarde au lieu de référencer un modèle existant.

mutationToDom et domToMutation

mutationToDom et domToMutation sont des hooks de sérialisation qui fonctionnent avec de l'ancien système de sérialisation XML. N'utilisez ces hooks que si cela est nécessaire (par exemple, travaillant sur un ancien codebase qui n'a pas encore été migré), sinon utilisez saveExtraState et loadExtraState.

mutationToDom renvoie un nœud XML qui représente l'état supplémentaire du bloc, et domToMutation accepte le même nœud XML et applique l'état à dans le bloc.

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

Le code XML obtenu se présente comme suit:

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

Si votre fonction mutationToDom renvoie une valeur nulle, aucun élément supplémentaire ne sera dans le fichier XML.

Crochets d'interface utilisateur

Si vous fournissez certaines fonctions dans votre mutateur, Blockly ajoute un "mutateur" par défaut à votre bloc.

Vous n'avez pas besoin d'utiliser cette interface utilisateur si vous souhaitez ajouter une sérialisation supplémentaire. Vous pourriez utilisez une interface utilisateur personnalisée, comme la fonction blocs-plus-moins plug-in ou vous pourriez n'utiliser aucune UI !

compose et décompose

L'interface utilisateur par défaut repose sur les fonctions compose et decompose.

decompose "explose" le bloc en sous-blocs plus petits, supplémentaires, ajoutées et supprimées. Cette fonction doit renvoyer un "bloc supérieur" qui est le bloc principal de l'espace de travail du mutateur auquel les sous-blocs se connectent.

compose interprète ensuite la configuration des sous-blocs et les utilise pour modifier le bloc principal. Cette fonction doit accepter le "bloc supérieur" qui était renvoyé par decompose en tant que paramètre.

Notez que ces fonctions sont "mélangées" au bloc "muté" donc this pour faire référence à ce bloc.

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

Vous pouvez également définir une fonction saveConnections compatible avec l'interface utilisateur par défaut. Cette fonction vous permet d'associer les enfants bloc principal (qui existe dans l'espace de travail principal) avec des sous-blocs présents dans l'espace de travail du mutateur. Vous pouvez ensuite utiliser ces données pour vous assurer que votre compose relie correctement les enfants de votre bloc principal lorsque votre les sous-blocs sont réorganisés.

saveConnections doit accepter le "bloc supérieur" renvoyées par votre decompose en tant que paramètre. Si la fonction saveConnections est définie, Blockly l'appellera avant d'appeler 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();
  }
},

Enregistrement…

Les mutateurs ne sont qu'un type d'extension spécial. Ils doivent donc également être enregistrées avant de pouvoir les utiliser dans le fichier JSON de votre type de bloc définition.

// 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: chaîne à associer au mutateur pour pouvoir l'utiliser au format JSON.
  • mixinObj: objet contenant les différentes méthodes de mutation. Exemple : saveExtraState et loadExtraState.
  • opt_helperFn: fonction d'assistance facultative qui s'exécuter sur le bloc après le mélange.
  • opt_blockList: tableau facultatif de types de blocs (sous forme de chaînes) qui ajouté au menu déroulant dans l'UI du mutateur par défaut, si les méthodes d'UI sont également définis.

Notez que, contrairement aux extensions, chaque type de bloc ne peut avoir qu'un seul mutateur.

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

Fonction d'assistance

Avec le mixin, un mutateur peut enregistrer une fonction d'assistance. Cette fonction est sur chaque bloc du type donné après sa création et que le mixinObj est ajouté. Elle permet d'ajouter des déclencheurs ou des effets à une mutation.

Par exemple, vous pouvez ajouter un assistant à votre bloc de type liste qui définit nombre initial d'articles:

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