Ajouter un champ de plug-in à Block Factory

Les outils pour les développeurs Blockly vous permettent de créer des blocs personnalisés à l'aide de blocs. Il est compatible avec les champs publiés en tant que plug-ins, en plus de ceux fournis avec le cœur de Blockly. Si vous avez créé un champ personnalisé, vous pouvez ajouter sa prise en charge à la fabrique de blocs en suivant ce guide. Le champ personnalisé doit être publié sur npm avant que vous puissiez ajouter la prise en charge. Vous devez également vous engager à mettre à jour votre champ pour suivre les modifications apportées à Blockly. Sinon, nous devrons peut-être le supprimer de la fabrique de blocs à l'avenir.

Développement sur la Block Factory

Le code source de la Block Factory se trouve dans le dépôt blockly-samples, dans le répertoire examples/developer-tools.

Pour envoyer une modification aux outils pour les développeurs dans blockly-samples, vous devez suivre les étapes habituelles pour développer dans blockly-samples. Contrairement à l'utilisation de plug-ins, vous devrez exécuter npm install directement à partir du répertoire examples/developer-tools, plutôt qu'au niveau racine de blockly-samples.

Installer le plug-in

Pour que la fabrique de blocs affiche votre champ personnalisé dans l'aperçu, elle doit l'installer. Ajoutez votre champ en tant que dépendance npm des outils pour les développeurs. Ensuite, enregistrez-le ou effectuez toute autre configuration nécessaire dans developer-tools/src/blocks/index.ts.

Créer un bloc pour le champ

Étant donné que la fabrique de blocs utilise des blocs pour créer des blocs personnalisés, vous aurez besoin d'un bloc qui représente votre champ personnalisé.

Créer la définition du bloc

Vous devez concevoir le bloc pour votre champ. Si vous souhaitez obtenir des métadonnées, vous pouvez même le concevoir à l'aide de Block Factory. Le bloc doit permettre à l'utilisateur de configurer la configuration requise par votre champ, comme les valeurs par défaut et un nom. Ajoutez cette définition de bloc à developer-tools/src/blocks/fields.ts et importez-la dans developer-tools/src/blocks/index.ts.

Ajouter un bloc à la boîte à outils

Ensuite, vous devez ajouter ce bloc à la définition de la boîte à outils pour le rendre accessible aux utilisateurs. La définition de la boîte à outils se trouve dans developer-tools/src/toolbox.ts. Votre bloc doit être ajouté à la catégorie "Champs".

Générateurs de code

La fabrique de blocs fonctionne à l'aide du système de générateur de code que vous connaissez déjà dans Blockly. Chaque bloc possède un générateur de code de bloc pour chaque type de sortie généré par la fabrique de blocs, et les blocs parents assemblent le code des blocs enfants dans la sortie appropriée. Pour ajouter la prise en charge d'un champ personnalisé, vous devez ajouter des fonctions de générateur de code de bloc pour chacune des classes Code Generator.

Créez un fichier pour votre bloc de champ dans le répertoire output-generators/fields. Vous ajouterez les générateurs de code par blocs pour chacun des générateurs suivants à ce fichier. Importez ce fichier dans le fichier blocks/index.ts afin que les fonctions du générateur de code par blocs soient chargées dans l'application.

Définition de JavaScript

javascriptDefinitionGenerator crée le code qui sera inclus dans la définition JavaScript d'un bloc qui inclut votre champ personnalisé. En général, cela signifie que le générateur de code par blocs doit renvoyer une ligne de code qui ressemble à .appendField(new YourFieldConstructor(arg1, arg2), 'userSpecifiedName'). Notez que cette ligne de code n'inclut pas de point-virgule, car une entrée contenant plusieurs champs aura plusieurs appels à appendField enchaînés. Les arguments du constructeur sont extraits des valeurs définies par l'utilisateur dans le bloc de champ. Voici un exemple de ce générateur de code par blocs pour FieldAngle :

javascriptDefinitionGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: JavascriptDefinitionGenerator,
): string {
  const name = generator.quote_(block.getFieldValue('FIELDNAME'));
  const angle = block.getFieldValue('ANGLE');
  return `.appendField(new FieldAngle(${angle}), ${name})`;
};

Le bloc d'angle que l'utilisateur a fait glisser depuis la catégorie "Champs" de la boîte à outils Block Factory comporte deux champs :

  • FIELDNAME : l'utilisateur peut définir le nom du champ sur son bloc personnalisé.
  • ANGLE : l'utilisateur peut définir la valeur d'angle par défaut.

Dans ce générateur de code par blocs, nous obtenons la valeur d'angle par défaut et la transmettons comme seul argument au constructeur FieldAngle. Le nom du champ est toujours transmis en tant que deuxième argument à appendField.

Définition JSON

jsonDefinitionGenerator est similaire, mais il génère la partie de la définition du bloc JSON qui correspond à votre champ. En général, ce code est un objet JSON qui inclut :

  • type : correspond au nom de votre champ dans le registre des champs Blockly.
  • name : l'utilisateur peut définir le nom du champ sur son bloc personnalisé.
  • toutes les autres propriétés personnalisées nécessaires à la méthode d'initialisation JSON de votre champ.

Voici un autre exemple tiré de FieldAngle :

jsonDefinitionGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: JsonDefinitionGenerator,
): string {
  const code = {
    type: 'field_angle',
    name: block.getFieldValue('FIELDNAME'),
    angle: block.getFieldValue('ANGLE'),
  };
  return JSON.stringify(code);
};

En-têtes de code

Le générateur d'en-têtes de code crée la sortie d'en-têtes de code affichée dans Block Factory. Cette sortie peut être basculée entre les importations esmodule et les balises de script, selon la façon dont l'utilisateur souhaite charger le code. Il existe donc en fait deux instances de générateur différentes : une pour chaque cas. Vous devez ajouter un générateur de code de bloc pour chacun d'eux. Voici un exemple pour FieldAngle :

importHeaderGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: CodeHeaderGenerator,
): string {
  generator.addHeaderLine(
    `import {registerFieldAngle, FieldAngle} from '@blockly/field-angle';`,
  );
  generator.addHeaderLine(`registerFieldAngle();`);
  return '';
};

scriptHeaderGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: CodeHeaderGenerator,
): string {
  generator.addHeaderLine(
    `<script src="https://unpkg.com/@blockly/field-angle"></script>`,
  );
  generator.addHeaderLine(`registerFieldAngle();`);
  return '';
};

Ces générateurs disposent d'une méthode appelée addHeaderLine qui vous permet de spécifier une ligne de code à appeler avant que votre champ ne soit utilisé dans le code. Cela inclut généralement des tâches telles que l'importation du champ ou son chargement via une balise de script, et peut-être l'appel d'une fonction qui enregistrera le champ auprès du registre de champs de Blockly.

Pour ces deux générateurs de code par blocs, tout le code doit être ajouté par le biais d'appels à addHeaderLine. Cette fonction garantit que chaque ligne d'en-tête n'est affichée qu'une seule fois, même si votre bloc de champs personnalisés est utilisé plusieurs fois dans un même bloc personnalisé. Le générateur de code de bloc doit renvoyer la chaîne vide.

Stub de générateur

Enfin, nous avons le générateur qui crée le stub du générateur pour le champ. Dans ce générateur de code par blocs, vous allez écrire du code qui génère du code qui aide l'utilisateur à écrire du code qui génère du code. Vous êtes perdu ? C'est plus simple que vous ne le pensez !

Le stub du générateur pour un bloc personnalisé inclut une variable prédéfinie représentant chaque champ du bloc. Ensuite, TODO l'utilisateur doit terminer d'assembler toutes ces variables dans la chaîne de code finale que son bloc personnalisé renverra. Cela signifie que, en règle générale, tout ce que votre générateur de code par blocs doit faire est de renvoyer la ligne qui crée cette variable personnalisée. Imaginons que l'utilisateur crée un bloc personnalisé qui ajoute des rayons de soleil à son canevas. Ils ajoutent un champ d'angle au bloc et le nomment "SUN_DIRECTION". Le stub du générateur pour ce bloc inclurait la ligne const angle_sun_direction = block.getFieldValue("SUN_DIRECTION");. Il s'agit de la ligne de code que notre générateur de code par blocs doit renvoyer pour le champ d'angle :

generatorStubGenerator.forBlock['field_angle'] = function (
  block: Blockly.Block,
  generator: GeneratorStubGenerator,
): string {
  const name = block.getFieldValue('FIELDNAME');
  const fieldVar = generator.createVariableName('angle', name);
  return `const ${fieldVar} = block.getFieldValue(${generator.quote_(
    name,
  )});\n`;
};

Pour obtenir un nom standardisé pour la variable, vous pouvez appeler generator.createVariableName et transmettre le type de votre champ (tel que angle, number, etc.) ainsi que le nom que l'utilisateur a donné à son champ.

Tester

Une fois que vous avez écrit tous ces éléments, vous devriez pouvoir démarrer Block Factory en exécutant npm start dans le répertoire blockly-samples/examples/developer-tools. Vous devriez pouvoir faire glisser votre bloc depuis la catégorie de champ, l'ajouter à une entrée sur un bloc et voir la sortie changer. Vérifiez que l'aperçu du bloc est correct et que le code de chacune des sections de sortie est correct.