Modifier les définitions de bloc

Une question courante est de savoir comment modifier la définition d'un bloc existant. Par exemple, vous pouvez ajouter une vérification de connexion ou convertir un champ en entrée de valeur.

En général, il n'est pas possible de modifier une définition de bloc en place.

Modifier une définition existante

Si vous souhaitez modifier la définition d'un type de bloc existant:

  1. Créez une copie de la définition existante, y compris des générateurs de code de bloc.
  2. Modifiez le texte et donnez un nouveau nom à votre type.
  3. Ajoutez votre nouvelle définition à Blockly.Blocks.

Pourquoi ne puis-je pas modifier directement une définition existante ?

Si vous souhaitez savoir pourquoi vous ne pouvez pas modifier une définition existante, lisez la suite. Nous allons examiner quelques possibilités.

Modifier directement une définition existante

Il existe deux façons de modifier directement la définition d'un bloc existant : le monkey-patching et le forking du code. Nous vous déconseillons vivement de procéder de cette manière, car vous risquez de casser le code qui dépend du code modifié ou du code dérivé. Ces deux techniques rendent également difficile l'intégration des mises à jour et des corrections de bugs. Pour en savoir plus, consultez Qu'est-ce que le monkey-patching ? et Dupliquer Blockly.

Créer une sous-classe d'une définition existante

Vous pouvez envisager de créer une sous-classe d'une définition existante. Malheureusement, cela n'est pas possible, car les définitions de bloc ne sont pas des classes, mais des mixins. Par exemple, il n'est pas possible d'écraser une propriété de couleur, car la définition ne comporte pas de propriété de couleur. À la place, il comporte une fonction init qui appelle setColour pour définir la propriété de couleur sur le bloc. Étant donné que l'appel se trouve dans init, il n'existe aucun moyen de le remplacer sans remplacer l'intégralité de la fonction init.

Écraser une fonction dans une définition existante

Vous pouvez écraser une fonction dans une définition existante:

Blockly.Blocks['existing_block'].init = function() {/*new function*/};

Cela fonctionne, mais vous devez copier et modifier la fonction existante. Vous ne pouvez pas remplacer une seule ligne par magie. Cette approche pose plusieurs problèmes:

  • Cela ne diffère guère de la copie et de la modification de la définition entière.
  • Vous ne pouvez pas l'utiliser pour modifier la fonction init des blocs définis en JSON, tels que les blocs intégrés de Blockly. En effet, il n'y a pas de fonction init à copier, car elle est générée au moment de l'exécution.
  • Vous devez définir votre bloc à l'aide de JavaScript, ce qui peut entraîner des problèmes de localisation.

Écraser les résultats d'init

Une façon de "remplacer une seule ligne" d'une fonction init consiste à remplacer la fonction init par une fonction qui appelle la fonction init d'origine, puis écrase le résultat de cet appel. Par exemple, le code suivant modifie la couleur du bloc logic_null:

const originalInit = Blockly.Blocks['logic_null'].init;
Blockly.Blocks['logic_null'].init = function() {
  originalInit.call(this);
  this.setColour(300);
}

Malheureusement, cette méthode est moins utile qu'il n'y paraît. Exemple :

  • Élargir les vérifications de connexion ou appliquer un validateur de champ moins restrictif peut invalider les hypothèses faites par les générateurs de code de bloc et les gestionnaires d'événements.

  • Remplacer un champ par une valeur saisie casse les générateurs de code de bloc et les validateurs de champ, et peut casser les gestionnaires d'événements. Cela peut également être extrêmement difficile à faire pour les blocs localisés, car différents paramètres régionaux peuvent entraîner des blocs avec des types et des ordres différents d'entrées et de champs.

Remplacer une paire clé-valeur dans une définition JSON

Si le code JSON d'un bloc est publiquement accessible, il est possible d'écraser des valeurs JSON individuelles. Exemple :

// Block definition.
blockJson = {...};
Blockly.Blocks['my_block'] = {
  init: function() {
    initJson(blockJson); // Called when the block is created.
  }
}

// Third-party code.
blockJson.colour = 100;

Toutefois, cela ne fonctionne que si l'objet JSON est accessible au public, que la définition définit explicitement la fonction init et que la fonction init appelle initJson. Cela ne fonctionne pas si le fichier JSON est transmis à defineBlocksWithJsonArray ou createBlockDefinitionsFromJsonArray, car il est traité avant qu'un tiers n'ait la possibilité de le modifier. (Notez que les blocs intégrés de Blockly utilisent createBlockDefinitionsFromJsonArray.)

Mais que faire si le fichier JSON n'est pas défini de cette manière ? Ne devrait-il pas être possible de remplacer les propriétés JSON ? Malheureusement, non. La définition contient une fonction init (et non JSON), et il n'existe aucune fonction permettant de convertir une fonction init en JSON.

// Doesn't work. There is no getJson() function.
const json = Blockly.Blocks['existing_block'].getJson();
json['message0'] = 'my new message0';
Blockly.Blocks['existing_block'].init = function () {
  initJson(json);
};

Concevoir pour la réutilisation

Lorsque vous concevez vos propres blocs personnalisés, vous pouvez les concevoir de manière à favoriser la réutilisation.

Réutiliser le fichier JSON

Si vous avez deux blocs sensiblement similaires, vous pouvez créer une définition JSON parente et la réutiliser dans les définitions enfants. Exemple :

const parentJson = {
  // shared properties
};

Blockly.Blocks['child_block_1'] = {
  init: function() {
    initJson({...parentJson, colour: 100})
  }
}

Blockly.Blocks['child_block_2'] = {
  init: function() {
    initJson({...parentJson, colour: 200})
  }
}

Vous pouvez également définir votre fichier JSON dans un objet accessible publiquement et transmettre cet objet à initJson dans votre fonction init. Cela permet à d'autres utilisateurs d'écraser des propriétés individuelles. Pour en savoir plus, consultez la section Remplacer une paire clé-valeur dans une définition JSON.

Réutiliser les fonctions

Les blocs peuvent définir un certain nombre de fonctions standards, telles que des gestionnaires d'événements au niveau du bloc, des info-bulles personnalisées, des validateurs de champ et les fonctions utilisées par les mutators, ainsi que des fonctions qui fournissent un comportement personnalisé, comme une fonction qui définit les valeurs de champ à partir de données externes, comme la position actuelle du bras d'un robot.

Il est possible de réutiliser ces fonctions dans plusieurs blocs.

Utiliser un champ déroulant

Si vous disposez d'un ensemble de blocs sensiblement identiques, à l'exception d'un opérateur, vous pouvez concevoir un seul bloc avec un champ déroulant pour l'opérateur. Exemple :

  • Le bloc logic_operation intégré utilise un menu déroulant avec les opérateurs and et or.
  • Le bloc math_arithmetic intégré utilise une liste déroulante avec les opérateurs +, -, ×, ÷ et ^.

Écrire des générateurs de code pour de tels blocs est généralement un peu plus complexe, mais toujours plus facile que d'écrire et de gérer plusieurs blocs.

Utiliser un mutateur

Si vous disposez d'un ensemble de blocs représentant différentes variantes de la même structure de programmation, vous pouvez créer un seul bloc qui utilise un modificateur. Par exemple, le bloc controls_if intégré peut représenter plusieurs variantes d'instructions if-then-else.