Estensioni e mutatori

Le estensioni sono funzioni che vengono eseguite su ciascun blocco di un determinato tipo è stato creato. Spesso aggiungono comportamenti o configurazioni personalizzati a un blocco.

Un mutatore è un tipo speciale di estensione che aggiunge la serie personalizzata, e a volte nell'interfaccia utente.

Estensioni

Le estensioni sono funzioni che vengono eseguite su ciascun blocco di un determinato tipo è stato creato. Può aggiungere una configurazione personalizzata (ad es. impostare la descrizione comando del blocco) oppure un comportamento personalizzato (ad es. l'aggiunta di un listener di eventi al blocco).

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

Le estensioni devono essere "registrate" in modo che possano essere associate a una stringa chiave. Poi puoi assegnare questa chiave stringa alla proprietà extensions del tuo JSON del tipo di blocco per applicare al blocco.

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

Puoi anche aggiungere più estensioni contemporaneamente. Tieni presente che extensions deve essere un array, anche se applichi una sola estensione.

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

Mixin

Blockly offre anche un metodo pratico per le situazioni in cui vuoi aggiungere proprietà/funzioni helper in un blocco, ma non eseguirle immediatamente. Questo ti permette di registrare un mixin contenente tutti i tuoi metodi/proprietà aggiuntivi. L'oggetto mixin viene poi aggregato in una funzione che applica il mixin ogni volta che un'istanza viene creato il tipo di blocco specificato.

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

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

È possibile fare riferimento alle chiavi stringa associate ai mixin in JSON come qualsiasi altro .

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

Mutatori

Un mutatore è un tipo speciale di estensione che aggiunge un'ulteriore serializzazione (extra che viene salvato e caricato) in un blocco. Ad esempio, lo strumento I blocchi controls_if e list_create_with richiedono una serializzazione aggiuntiva per possono salvare quanti input hanno.

Tieni presente che cambiare la forma del blocco non significa necessariamente che un'ulteriore serializzazione. Ad esempio, il blocco math_number_property cambia di forma, ma lo fa in base a un campo a discesa, il cui valore viene già serializzato. Di conseguenza, può utilizzare solo un campo strumento di convalida e non serve un mutatore.

Consulta la serializzazione per maggiori informazioni su quando e quando è necessario un mutatore.

I mutatori forniscono inoltre una UI integrata che permette agli utenti di cambiare la forma dei blocchi se fornisci alcuni metodi facoltativi.

Hook di serializzazione

I mutatori hanno due coppie di hook di serializzazione con cui funzionano. Un paio di ganci funziona con il nuovo sistema di serializzazione JSON, mentre l'altra coppia funziona con un vecchio sistema di serializzazione XML. Devi fornire almeno una di queste coppie.

salvaExtraState e loadExtraState

saveExtraState e loadExtraState sono hook di serializzazione che funzionano con nuovo sistema di serializzazione JSON. saveExtraState restituisce un file JSON serializzabile che rappresenta lo stato aggiuntivo del blocco e loadExtraState accetta lo stesso valore serializzabile JSON e lo applica al blocco.

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

Il risultato JSON sarà simile a questo:

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

Se il blocco è nello stato predefinito quando è serializzato, Il metodo saveExtraState può restituire null per indicarlo. Se le tue Il metodo saveExtraState restituisce null, quindi nessuna proprietà extraState viene aggiunta a il file JSON. In questo modo le dimensioni del file salvato saranno ridotte.

Serializzazione e backup completi dei dati

saveExtraState riceve anche un parametro doFullSerialization facoltativo. Questo è utilizzato da blocchi che fanno riferimento allo stato serializzato da un serializzatore (come i modelli dei dati di backup). Il parametro segnala che lo stato di riferimento non sarà disponibile quando il blocco viene deserializzato, quindi deve serializzare tutto lo stato di supporto. Ad esempio, true quando un singolo blocco viene serializzato o quando un blocco viene copiato e incollato.

Due casi d'uso comuni sono:

  • Quando un singolo blocco viene caricato in un'area di lavoro in cui i dati di supporto modello non esiste, ha informazioni sufficienti nel proprio stato per per creare un nuovo modello dei dati.
  • Quando un blocco viene copiato e incollato, viene sempre creato un nuovo supporto del modello dei dati, invece di farvi riferimento a uno esistente.

Alcuni blocchi che utilizzano questo metodo Blocchi @blockly/block-shareable-procedures. Normalmente serializzano un riferimento a un modello dei dati di supporto, che archivia il loro stato. Se invece il parametro doFullSerialization è true, serializzano tutti il proprio stato. I blocchi di procedura condivisibili lo utilizzano per garantire che, vengono copiati e incollati creano un nuovo modello dei dati di supporto, anziché fare riferimento a un un modello esistente.

mutationToDom e domToMutation

mutationToDom e domToMutation sono hook di serializzazione che funzionano con un vecchio sistema di serializzazione XML. Usa questi ganci solo se necessario (ad es. se devi lavorare su un vecchio codebase di cui non è stata ancora eseguita la migrazione), altrimenti usa saveExtraState e loadExtraState.

mutationToDom restituisce un nodo XML che rappresenta lo stato aggiuntivo del bloccare e domToMutation accetta lo stesso nodo XML e applica lo stato a bloccare il blocco.

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

Il codice XML risultante avrà il seguente aspetto:

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

Se la funzione mutationToDom restituisce un valore nullo, non sarà presente nessun elemento aggiuntivo. aggiunti al file XML.

Hook UI

Se fornisci determinate funzioni come parte del mutatore, Blockly aggiungerà un "mutatore" predefinito interfaccia utente per il tuo blocco.

Non è necessario utilizzare questa UI se vuoi aggiungere un'ulteriore serializzazione. Potresti usano una UI personalizzata, come blocchi-più-meno plug-in o non potresti utilizzare alcuna UI.

comporre e scomporre

La UI predefinita si basa sulle funzioni compose e decompose.

decompose "esplode" il blocco in blocchi secondari più piccoli che possono essere spostati aggiunti ed eliminati. Questa funzione dovrebbe restituire un "blocco superiore" ovvero il blocco principale nell'area di lavoro del mutatore a cui si connettono i sottoblocchi.

compose interpreta quindi la configurazione dei sottoblocchi e li utilizza per modificare il blocco principale. Questa funzione deve accettare il "blocco superiore" che era restituito da decompose come parametro.

Tieni presente che queste funzioni vengono "combinate" al blocco che viene "mutato" quindi this può essere utilizzato per fare riferimento a quel blocco.

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

Se vuoi, puoi anche definire una funzione saveConnections che funziona con l'interfaccia utente predefinita. Questa funzione ti offre la possibilità di associare i tuoi figli principale (che esiste nell'area di lavoro principale) con blocchi secondari esistenti nell'area di lavoro del mutatore. Puoi quindi utilizzare questi dati per verificare che compose funzioni correttamente ricollega gli elementi secondari del blocco principale quando vengono riorganizzati.

saveConnections deve accettare il "blocco superiore" restituito da decompose come parametro. Se la funzione saveConnections è definita, Blockly lo chiamerà prima di chiamare il numero 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();
  }
},

In fase di registrazione

I mutatori sono solo un particolare tipo di estensione, quindi devono essere registrati prima che tu possa utilizzarli in JSON del tuo tipo di blocco definizione.

// 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: una stringa da associare al mutatore per poterla utilizzare in JSON.
  • mixinObj: un oggetto contenente i vari metodi di mutazione. Ad es. saveExtraState e loadExtraState.
  • opt_helperFn: una funzione helper facoltativa che devi eseguire sul blocco dopo aver mixato il mix.
  • opt_blockList: un array facoltativo di tipi di blocchi (come stringhe) che verranno aggiunti al riquadro a comparsa nell'interfaccia utente del mutatore predefinito, se anche i metodi UI sono definito.

Tieni presente che, a differenza delle estensioni, ogni tipo di blocco può avere un solo mutatore.

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

Funzione helper

Insieme al mixin, un mutatore può registrare una funzione aiutante. Questa funzione è viene eseguito su ciascun blocco del tipo specificato dopo la sua creazione e il mixinObj aggiunto. Può essere usato per aggiungere ulteriori trigger o effetti a una mutazione.

Ad esempio, potresti aggiungere un elemento di supporto al blocco simile a un elenco che imposti numero iniziale di elementi:

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