Fazer upgrade de um campo personalizado

Em julho de 2019 (versão 2.20190722), foi adicionada uma API de campos mais codificada. O objetivo é que ele seja o mais compatível possível com versões anteriores. Isso significa que, se você criou um campo personalizado antes de julho de 2019, ele provavelmente vai continuar funcionando. Antes de decidir se o campo personalizado precisa ser atualizado, leia a seção Áreas de risco e teste o campo completamente.

Como não havia padronização entre os campos antes de julho de 2019, é difícil abranger todas as mudanças que um desenvolvedor pode precisar fazer. Este documento tenta abordar todas as mudanças prováveis, mas se ele não abordar algo do seu interesse, leia a seção sobre receber assistência para upgrade.

Áreas de perigo

As áreas de risco são locais conhecidos em que a API mudou e seu campo pode ser interrompido.

Blockly.Field.register

Os campos não são mais registrados pelo Blockly.Field.register();. Agora há um namespace fieldRegistry que processa o registro.

Blockly.Field.register('my_field_name', myFieldClass);

O resultado é:

Blockly.fieldRegistry.register('my_field_name', myFieldClass);

setText

A função setText não é mais chamada pelo núcleo do Blockly. Portanto, se a função setText contiver lógica, ela precisará ser movida para o conjunto de funções de processamento de valores, a função getText e as funções de renderização (dependendo do que exatamente a função setText está fazendo).

CustomFields.UpgradeField.prototype.setText = function(newText) {
  // Do validation.
  if (typeof newText != 'string' || newText === this.text_) {
    return;
  }

  // Fire event.
  if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
    Blockly.events.fire(new Blockly.Events.BlockChange(
        this.sourceBlock_, 'field', this.name, this.text_, newText
    ));
  }

  // Update text value.
  this.text_ = 'prefix' + newText;

  // Rerender.
  this.size_.width = 0;
};

O resultado é:

CustomFields.UpgradeField.prototype.doClassValidation_ = function(newValue) {
  if (typeof newValue != 'string') {
    return null;
  }
  return newValue;
};

CustomFields.UpgradeField.prototype.getText = function() {
  return  'prefix' + this.value_;
}

O Blockly processa automaticamente:

  • Verificar se o novo valor é diferente do antigo.
  • Atualizando o valor.
  • Acionamento de eventos de mudança.
  • Renderizando o campo novamente.

Você vai precisar lidar com:

Os upgrades recomendados são locais em que a API de campo foi alterada, mas, se você optar por não fazer mudanças, seu campo provavelmente ainda vai funcionar.

SERIALIZABLE

Para mais informações sobre as propriedades EDITABLE e SERIALIZABLE, consulte Propriedades editáveis e serializáveis.

CustomFields.UpgradeField.prototype.SERIALIZABLE = true;

O aviso abaixo pode ser ignorado, mas você pode resolver o problema definindo a propriedade SERIALIZABLE:

Detected an editable field that was not serializable. Please define
SERIALIZABLE property as true on all editable custom fields. Proceeding
with serialization.

O aviso acima significa que o Blockly acredita que você quer que o campo seja serializado (porque a propriedade EDITABLE é verdadeira), mas não pode ter certeza até que você defina a propriedade SERIALIZABLE. Se você não fizer nada, tudo vai funcionar corretamente, e seu campo será serializado, mas você vai receber avisos no console.

size_.width

this.size_.width = 0;

O resultado é:

this.isDirty_ = true;

O aviso abaixo pode ser ignorado, mas você pode resolver o problema definindo a propriedade isDirty_ em vez de size_.width:

Deprecated use of setting size_.width to 0 to rerender a field. Set
field.isDirty_ to true instead.

O aviso acima significa que o Blockly detectou que você está usando um método antigo para renderizar novamente um campo e gostaria que você usasse o novo método.

Para mais informações sobre a propriedade isDirty_, consulte isDirty_.

init

A função init foi transformada em uma função de modelo para reduzir o código duplicado em subclasses.

CustomFields.UpgradeField.prototype.init = function() {
  if (this.fieldGroup_) {
    // Already initialized once.
    return;
  }

  // Call superclass.
  CustomFields.UpgradeField.superClass_.init.call(this);

  // Create DOM elements.
  this.extraDom_ = Blockly.utils.dom.createSvgElement('image',
      {
        'height': '10px',
        'width': '10px'
      });
  this.extraDom_.setAttributeNS('http://www.w3.org/1999/xlink',
      'xlink:href', 'image.svg');
  this.extraDom_.style.cursor = 'pointer';
  this.fieldGroup_.appendChild(this.extraDom_);

  // Bind events.
  this.mouseOverWrapper_ =
    Blockly.browserEvents.bind(
        this.getClickTarget_(), 'mouseover', this, this.onMouseOver_);
  this.mouseOutWrapper_ =
    Blockly.browserEvents.bind(
        this.getClickTarget_(), 'mouseout', this, this.onMouseOut_);

  // Render.
  this.setValue(this.getValue());
};

O resultado é:

CustomFields.UpgradeField.prototype.initView = function() {
  CustomFields.UpgradeField.superClass_.initView.call(this);

  this.extraDom_ = Blockly.utils.dom.createSvgElement('image',
      {
        'height': '10px',
        'width': '10px'
      });
  this.extraDom_.setAttributeNS('http://www.w3.org/1999/xlink',
      'xlink:href', 'image.svg');
  this.extraDom_.style.cursor = 'pointer';
  this.fieldGroup_.appendChild(this.extraDom_);
};

CustomFields.UpgradeField.prototype.bindEvents_ = function() {
  CustomFields.UpgradeField.superClass_.bindEvents_.call(this);

  this.mouseOverWrapper_ =
    Blockly.bindEvent_(
        this.getClickTarget_(), 'mouseover', this, this.onMouseOver_);
  this.mouseOutWrapper_ =
    Blockly.bindEvent_(
        this.getClickTarget_(), 'mouseout', this, this.onMouseOut_);
};

Isso significa que o Blockly agora processa automaticamente:

  • Verificar se o campo já foi inicializado.
  • Como criar o fieldGroup_.
  • Renderização do campo.
  • Tooltip de vinculação e eventos do editor de exibição.

Você vai precisar lidar com:

onMouseDown_

CustomFields.UpgradeField.prototype.onMouseDown_ = function(e) {
  // ...
};

O resultado é:

CustomFields.UpgradeField.prototype.showEditor_ = function() {
  // ...
}

Recomendamos que você substitua a função showEditor_ para processar cliques do mouse em vez da função onMouseDown_, já que isso permite que a entrada passe pelo sistema de gestos.

Para mais informações sobre editores, consulte Editores.

setValue

A função setValue agora é uma função de modelo para reduzir o código duplicado em subclasses. Se a função setValue tiver lógica, refatore-a para se adequar aos caminhos de processamento de valores descritos em Processamento de valores.

text_

Recomendamos que você nunca acesse ou atualize diretamente a propriedade text_ do seu campo. Em vez disso, use a função getText para acessar o texto legível do usuário do seu campo e a função setValue para atualizar o valor armazenado do seu campo.

Para mais informações sobre o valor de um campo e o texto dele, consulte Anatomia de um campo.

Receber assistência de upgrade

O que fornecer

Ao pedir ajuda, é melhor fazer perguntas específicas:

Não recomendado: "O que há de errado com este campo?"

Também não é recomendável: "Me ajude a fazer upgrade deste campo".

Recomendado: "O texto do campo não está sendo atualizado corretamente".

Também é necessário fornecer recursos para as pessoas que estão ajudando você. Esses arquivos precisam ser fáceis de usar para outras pessoas.

Não recomendado:

  • Imagens de código.
  • Código incompleto.

Recomendado:

  • Preencha o código do campo em um formato de texto.
  • Imagens de GIFs de mau comportamento em campo.
  • Etapas para reproduzir o comportamento incorreto do campo.
  • A versão do Blockly de que você está fazendo upgrade.

Onde postar

Poste perguntas sobre a atualização no fórum de desenvolvedores do Blockly.

Se você tiver certeza de que o problema está no núcleo do Blockly, também poderá postar um problema no GitHub do Blockly. Se você decidir postar um problema, preencha todas as informações solicitadas.