Crea blocchi di procedure personalizzate

La creazione di blocchi di procedure personalizzate richiede:

  1. Installa il plug-in @blockly/block-shareable-procedures, come descritto nella pagina delle procedure di utilizzo.
  2. Utilizza il sistema di serializzazione JSON, come spiegato nella pagina della panoramica.

Aggiungi modelli dei dati all'area di lavoro

Sia la definizione della procedura sia i blocchi dei chiamanti di procedura fanno riferimento a un modello di dati di supporto che definisce la firma della procedura (nome, parametri e ritorno). Ciò consente una maggiore flessibilità nel modo in cui progetti l'applicazione (ad esempio, potresti consentire di definire le procedure in un'area di lavoro e farvi riferimento in un'altra).

Ciò significa che dovrai aggiungere i modelli dei dati di procedura all'area di lavoro affinché i tuoi blocchi funzionino. Ci sono vari modi per farlo (ad es. UI personalizzate).

Per farlo, @blockly/block-shareable-procedures, perché i blocchi di definizione delle procedure creano dinamicamente i propri modelli di dati di supporto quando vengono creati un'istanza nell'area di lavoro. Per implementarla personalmente, crea il modello in init ed eliminalo in destroy.

import {ObservableProcedureModel} from '@blockly/block-shareable-procedures';

Blockly.Blocks['my_procedure_def'] = {
  init: function() {
    this.model = new ObservableProcedureModel('default name');
    this.workspace.getProcedureMap().add(model);
    // etc...
  }

  destroy: function() {
    // (Optionally) Destroy the model when the definition block is deleted.

    // Insertion markers reference the model of the original block.
    if (this.isInsertionMarker()) return;
    this.workpace.getProcedureMap().delete(model.getId());
  }
}

Restituisci informazioni sui blocchi

La definizione della procedura e i blocchi delle chiamate di procedura devono implementare i metodi getProcedureModel, isProcedureDef e getVarModels. Questi sono gli hook utilizzati dal codice di Blockly per ottenere informazioni sui blocchi delle tue procedure.

Blockly.Blocks['my_procedure_def'] = {
  getProcedureModel() {
    return this.model;
  },

  isProcedureDef() {
    return true;
  },

  getVarModels() {
    // If your procedure references variables
    // then you should return those models here.
    return [];
  },
};

Blockly.Blocks['my_procedure_call'] = {
  getProcedureModel() {
    return this.model;
  },

  isProcedureDef() {
    return false;
  },

  getVarModels() {
    // If your procedure references variables
    // then you should return those models here.
    return [];
  },
};

Attiva il rendering sugli aggiornamenti

La definizione della procedura e i blocchi delle chiamate di procedura devono implementare il metodo doProcedureUpdate. Questo è l'hook chiamato dai modelli di dati per indicare ai blocchi di procedura di eseguire nuovamente il rendering.

Blockly.Blocks['my_procedure_def'] = {
  doProcedureUpdate() {
    this.setFieldValue('NAME', this.model.getName());
    this.setFieldValue(
        'PARAMS',
        this.model.getParameters()
            .map((p) => p.getName())
            .join(','));
    this.setFieldValue(
        'RETURN', this.model.getReturnTypes().join(',');
  }
};

Blockly.Blocks['my_procedure_call'] = {
  doProcedureUpdate() {
    // Similar to the def block above...
  }
};

Aggiungi serializzazione personalizzata

La serializzazione per i blocchi di procedure deve eseguire due operazioni separate.

  1. Durante il caricamento da JSON, i blocchi devono prendere un riferimento al loro modello dei dati di supporto, poiché i blocchi e i modelli sono serializzati separatamente.
  2. Quando copi e incolli un blocco di procedure, questo deve serializzare l'intero stato del suo modello di procedura, in modo che possa essere replicato.

Entrambe le cose vengono gestite tramite saveExtraState e loadExtraState. Ancora una volta, i blocchi di procedure personalizzate sono supportati solo quando si utilizza il sistema di serializzazione JSON, quindi dobbiamo solo definire gli hook di serializzazione JSON.

import {
    ObservableProcedureModel,
    ObservableParameterModel,
    isProcedureBlock
} from '@blockly/block-shareable-procedures';

Blockly.Blocks['my_procedure_def'] = {
  // When doFullSerialization is true, we should serialize the full state of
  // the model.
  saveExtraState(doFullSerialization) {
    const state = Object.create(null);
    state['procedureId']: this.model.getId();

    if (doFullSerialization) {
      state['name'] = this.model.getName();
      state['parameters'] = this.model.getParameters().map((p) => {
        return {name: p.getName(), p.getId()};
      });
      state['returnTypes'] = this.model.getReturnTypes();

      // Flag for deserialization.
      state['createNewModel'] = true;
    }

    return state;
  },

  loadExtraState(state) {
    const id = state['procedureId']
    const map = this.workspace.getProcedureMap();

    if (map.has(id) && !state['createNewModel']) {
      // Delete the existing model (created in init).
      map.delete(this.model.getId());
      // Grab a reference to the model we're supposed to reference.
      this.model = map.get(id);
      this.doProcedureUpdate();
      return;
    }

    // There is no existing procedure model (we are likely pasting), so
    // generate it from JSON.
    this.model
        .setName(state['name'])
        .setReturnTypes(state['returnTypes']);
    for (const [i, param] of state['parameters'].entries()) {
      this.model.insertParameter(
          i,
          new ObservableParameterModel(
              this.workspace, param['name'], param['id']));
    }
    this.doProcedureUpdate();
  },
};

Blockly.Blocks['my_procedure_call'] = {
  saveExtraState() {
    return {
      'procedureId': this.model.getId(),
    };
  },

  loadExtraState(state) {
    // Delete our existing model (created in init).
    this.workspace.getProcedureMap().delete(model.getId());
    // Grab a reference to the new model.
    this.model = this.workspace.getProcedureMap()
        .get(state['procedureId']);
    if (this.model) this.doProcedureUpdate();
  },

  // Handle pasting after the procedure definition has been deleted.
  onchange(event) {
    if (event.type === Blockly.Events.BLOCK_CREATE &&
        event.blockId === this.id) {
      if(!this.model) { // Our procedure definition doesn't exist =(
        this.dispose();
      }
    }
  }
};

Facoltativamente, modifica il modello della procedura

Puoi anche aggiungere la possibilità agli utenti di modificare il modello di procedura. La chiamata dei metodi insertParameter, deleteParameter o setReturnTypes attiverà automaticamente il rendering dei blocchi (tramite doProcedureUpdate).

Le opzioni per creare UI per modificare il modello di procedura includono l'utilizzo di mutatori (utilizzati dai blocchi della procedura integrata), campi immagine con gestori di clic, elementi completamente esterni a Blockly e così via.

Aggiungi blocchi al riquadro degli strumenti

La categoria di procedura dinamica integrata di Blockly è specifica per i blocchi di procedura integrati di Blockly. Per poter accedere ai blocchi, devi definire la tua categoria dinamica personalizzata e aggiungerla ai tuoi strumenti.

const proceduresFlyoutCallback = function(workspace) {
  const blockList = [];
  blockList.push({
    'kind': 'block',
    'type': 'my_procedure_def',
  });
  for (const model of
        workspace.getProcedureMap().getProcedures()) {
    blockList.push({
      'kind': 'block',
      'type': 'my_procedure_call',
      'extraState': {
        'procedureId': model.getId(),
      },
    });
  }
  return blockList;
};

myWorkspace.registerToolboxCategoryCallback(
    'MY_PROCEDURES', proceduresFlyoutCallback);