Menus de contexto

Um menu de contexto contém uma lista de ações que um usuário pode realizar em um componente, como um espaço de trabalho, um bloco ou um comentário do espaço de trabalho. O menu de contexto é mostrado em resposta a um clique com o botão direito do mouse ou um toque longo em um dispositivo touchscreen. Se você usa o plug-in @blockly/keyboard-navigation, ele também aparece com um atalho de teclado, que é Ctrl+Enter no Windows ou Command+Enter no Mac.

O menu de contexto padrão de um bloco

Os menus contextuais são um bom lugar para adicionar ações que o usuário realiza com pouca frequência, como baixar uma captura de tela. Se você acha que uma ação será usada com mais frequência, crie uma maneira mais fácil de invocá-la.

Os menus de contexto são compatíveis com espaços de trabalho, blocos, comentários do espaço de trabalho, balões e conexões. Você também pode implementá-los nos seus próprios componentes personalizados. O Blockly oferece menus de contexto padrão que podem ser personalizados. Também é possível personalizar menus de contexto em espaços de trabalho e blocos por espaço de trabalho ou por bloco.

Como os menus de contexto funcionam

O Blockly tem um registro que contém modelos para todos os itens de menu possíveis. Cada modelo descreve como construir um único item em um menu de contexto. Quando o usuário invoca um menu de contexto em um componente, ele:

  1. Pede ao registro para construir uma matriz de itens de menu que se aplicam ao componente. O registro pergunta a cada modelo se ele se aplica ao componente e, em caso afirmativo, adiciona um item de menu correspondente à matriz.

  2. Se o componente for um espaço de trabalho ou bloco, verifique se o espaço de trabalho ou bloco específico em que o menu foi invocado tem uma função para personalizar o menu de contexto. Se for, ele transmite a matriz para a função, que pode adicionar, excluir ou modificar elementos da matriz.

  3. Mostra o menu de contexto usando a matriz (possivelmente modificada) de itens do menu de contexto.

O Blockly define um conjunto padrão de modelos para os menus de contexto de espaços de trabalho, blocos e comentários do espaço de trabalho. Ele pré-carrega os modelos para espaços de trabalho e blocos no registro. Se quiser usar os modelos para comentários do espaço de trabalho, carregue-os no registro manualmente.

Para informações sobre como adicionar, excluir e modificar modelos no registro, consulte Personalizar o registro.

Escopo

Os menus de contexto são implementados por diferentes tipos de componentes, incluindo espaços de trabalho, comentários do espaço de trabalho, conexões, blocos, balões e seus próprios componentes personalizados. Os menus de contexto para cada um desses tipos de componentes podem conter itens diferentes, e os itens podem se comportar de maneira diferente com base no tipo de componente. Assim, o sistema de menu de contexto precisa saber em qual componente ele foi invocado.

Para resolver isso, o registro usa um objeto Scope. O componente em que o menu de contexto foi invocado é armazenado na propriedade focusedNode como um objeto que implementa IFocusableNode. (IFocusableNode é implementado por todos os componentes em que os usuários podem se concentrar, incluindo aqueles que implementam menus de contexto. Para mais informações, consulte Sistema de foco.

O objeto Scope é transmitido para várias funções em um modelo. Em qualquer função que receba um objeto Scope, você pode decidir o que fazer com base no tipo do objeto na propriedade focusedNode. Por exemplo, você pode verificar se o componente é um bloco com:

if (scope.focusedNode instanceof Blockly.BlockSvg) {
  // do something with the block
}

O objeto Scope tem outras propriedades opcionais que não são mais recomendadas para uso, mas ainda podem ser definidas:

  • block só é definido se o componente cujo menu é mostrado for um BlockSvg.
  • workspace só é definido se o componente for um WorkspaceSvg.
  • comment só é definido se o componente for um RenderedWorkspaceComment.

Essas propriedades não abrangem todos os tipos de componentes que podem ter um menu de contexto. Por isso, prefira usar a propriedade focusedNode.

O tipo RegistryItem

Os modelos têm o tipo ContextMenuRegistry.RegistryItem, que contém as seguintes propriedades. As propriedades preconditionFn, displayText e callback são mutuamente exclusivas com a propriedade separator.

ID

A propriedade id precisa ser uma string exclusiva que indica o que o item do menu de contexto faz.

const collapseTemplate = {
  id: 'collapseBlock',
  // ...
};

Função de pré-condição

Você pode usar o preconditionFn para restringir quando e como um item de menu de contexto deve ser exibido.

Ele precisa retornar uma das seguintes strings: 'enabled', 'disabled' ou 'hidden'.

Valor Descrição Imagem
'enabled' Mostra que o item está ativo. Uma opção ativada
'disabled' Mostra que o item não está ativo. Uma opção desativada
'hidden' Oculta o item.

O preconditionFn também recebe um Scope que pode ser usado para determinar em que tipo de componente o menu foi aberto e o estado desse componente.

Por exemplo, você pode querer que um item apareça apenas para blocos e somente quando esses blocos estiverem em um estado específico:

const collapseTemplate = {
  // ...
  preconditionFn: (scope) => {
    if (scope.focusedNode instanceof Blockly.BlockSvg) {
      if (!scope.focusedNode.isCollapsed()) {
        // The component is a block and it is not already collapsed
        return 'enabled';
      } else {
        // The block is already collapsed
        return 'disabled';
      }
    }
    // The component is not a block
    return 'hidden';
  },
  // ...
}

Texto de exibição

O displayText é o que deve ser mostrado ao usuário como parte do item de menu. O texto de exibição pode ser uma string, HTML ou uma função que retorna uma string ou HTML.

const collapseTemplate = {
  // ...
  displayText: 'Collapse block',
  // ...
};

Se você quiser mostrar uma tradução do Blockly.Msg, use uma função. Se você tentar atribuir o valor diretamente, as mensagens poderão não ser carregadas, e você vai receber um valor de undefined.

const collapseTemplate = {
  // ...
  displayText: () => Blockly.Msg['MY_COLLAPSE_BLOCK_TEXT'],
  // ...
};

Se você usar uma função, ela também vai receber um valor Scope. Você pode usar isso para adicionar informações sobre o elemento ao texto de exibição.

const collapseTemplate = {
  // ...
  displayText: (scope) => {
    if (scope.focusedNode instanceof Blockly.Block) {
      return `Collapse ${scope.focusedNode.type} block`;
    }
    // Shouldn't be possible, as our preconditionFn only shows this item for blocks
    return '';
  },
  // ...
}

Peso

O weight determina a ordem em que os itens do menu de contexto são mostrados. Valores mais positivos aparecem mais abaixo na lista do que valores menos positivos. É possível imaginar que itens com pesos maiores são "mais pesados" e, portanto, afundam até o fundo.

const collapseTemplate = {
  // ...
  weight: 10,
  // ...
}

Os pesos dos itens do menu de contexto integrado seguem uma ordem crescente, começando em 1 e aumentando em 1.

Função de callback

A propriedade callback é uma função que realiza a ação do item do menu de contexto. Ele recebe vários parâmetros:

  • scope: um objeto Scope que fornece uma referência ao componente com o menu aberto.
  • menuOpenEvent: o Event que acionou a abertura do menu de contexto. Isso pode ser um PointerEvent ou um KeyboardEvent, dependendo de como o usuário abriu o menu.
  • menuSelectEvent: o Event que selecionou este item específico do menu de contexto. Pode ser um PointerEvent ou KeyboardEvent, dependendo de como o usuário selecionou o item.
  • location: o Coordinate em coordenadas de pixel em que o menu foi aberto. Isso permite, por exemplo, criar um novo bloco no local do clique.
const collapseTemplate = {
  // ...
  callback: (scope, menuOpenEvent, menuSelectEvent, location) => {
    if (scope.focusedNode instanceof Blockly.BlockSvg) {
      scope.focusedNode.collapse();
    }
  },
}

É possível usar scope para criar modelos que funcionam de maneira diferente dependendo do componente em que foram abertos:

const collapseTemplate = {
  // ...
  callback: (scope) => {
    if (scope.focusedNode instance of Blockly.BlockSvg) {
      // On a block, collapse just the block.
      const block = scope.focusedNode;
      block.collapse();
    } else if (scope.focusedNode instanceof Blockly.WorkspaceSvg) {
      // On a workspace, collapse all the blocks.
      let workspace = scope.focusedNode;
      collapseAllBlocks(workspace);
    }
  }
}

Separador

A propriedade separator desenha uma linha no menu de contexto.

Modelos com a propriedade separator não podem ter propriedades preconditionFn, displayText ou callback e só podem ser definidos com a propriedade scopeType. A última restrição significa que eles só podem ser usados em menus de contexto para espaços de trabalho, blocos e comentários do espaço de trabalho.

const separatorAfterCollapseBlockTemplate = {
  id: 'separatorAfterCollapseBlock',
  scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
  weight: 11, // Between the weights of the two items you want to separate.
  separator: true,
};

Você precisa de um modelo diferente para cada separador no menu de contexto. Use a propriedade weight para posicionar cada separador.

Tipo de escopo

A propriedade scopeType foi descontinuada. Antes, ele era usado para determinar se um item de menu deveria ser mostrado em um menu de contexto para um bloco, um comentário do espaço de trabalho ou um espaço de trabalho. Como os menus de contexto podem ser abertos em outros componentes, a propriedade scopeType é muito restritiva. Em vez disso, use o preconditionFn para mostrar ou ocultar sua opção nos componentes correspondentes.

Se você tiver modelos de menu de contexto que usam scopeType, o Blockly continuará mostrando o item apenas para o componente adequado.

const collapseTemplate = {
  // ...
  scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
  // ...
};

Personalizar o registro

É possível adicionar, excluir ou modificar modelos no registro. Os modelos padrão estão em contextmenu_items.ts.

Adicionar um modelo

É possível adicionar um modelo ao registro ao registrá-lo. Faça isso uma vez no carregamento da página. Isso pode acontecer antes ou depois de você injetar seu espaço de trabalho.

const collapseTemplate = { /* properties from above */ };
Blockly.ContextMenuRegistry.registry.register(collapseTemplate);

Excluir um modelo

É possível remover um modelo do registro cancelando o registro dele por ID.

Blockly.ContextMenuRegistry.registry.unregister('someID');

Modificar um modelo

É possível modificar um modelo atual buscando-o no registro e fazendo as mudanças no local.

const template = Blockly.ContextMenuRegistry.registry.getItem('someID');
template?.displayText = 'some other display text';

Desativar menus de contexto de bloqueio

Por padrão, os blocos têm um menu de contexto que permite aos usuários adicionar comentários ou duplicar blocos.

Para desativar o menu de contexto de um bloco específico, faça o seguinte:

block.contextMenu = false;

Na definição JSON de um tipo de bloco, use a chave enableContextMenu:

{
  // ...,
  "enableContextMenu": false,
}

Personalizar menus de contexto por tipo de bloco ou espaço de trabalho

Depois que o Blockly gerar uma matriz de itens do menu de contexto, você poderá personalizar para blocos ou espaços de trabalho individuais. Para fazer isso, defina BlockSvg.customContextMenu ou WorkspaceSvg.configureContextMenu como uma função que modifica a matriz no lugar.

Os objetos na matriz transmitida para blocos têm o tipo ContextMenuOption ou implementam a interface LegacyContextMenuOption. Os objetos transmitidos para espaços de trabalho têm o tipo ContextMenuOption. O Blockly usa as seguintes propriedades desses objetos:

  • text: o texto de exibição.
  • enabled: se false, mostre o item com texto cinza.
  • callback: a função a ser chamada quando o item é clicado.
  • separator: o item é um separador. Mutuamente exclusivo com as outras três propriedades.

Consulte a documentação de referência para tipos de propriedade e assinaturas de função.

Por exemplo, veja uma função que adiciona um item Hello, World! ao menu de contexto de um espaço de trabalho:

workspace.configureContextMenu = function (menuOptions, e) {
  const item = {
    text: 'Hello, World!',
    enabled: true,
    callback: function () {
      alert('Hello, World!');
    },
  };
  // Add the item to the end of the context menu.
  menuOptions.push(item);
}

Mostrar um menu de contexto em um objeto personalizado

Para fazer com que os menus de contexto apareçam em componentes personalizados, siga estas etapas:

  1. Implemente IFocusableNode ou estenda uma classe que implemente IFocusableNode. Essa interface é usada no sistema de menu de contexto para identificar seu componente. Ele também permite que os usuários naveguem até o componente usando o plug-in de navegação por teclado.
  2. Implemente IContextMenu, que contém a função showContextMenu. Essa função recebe os itens do menu de contexto do registro, calcula o local na tela em que o menu será mostrado e, por fim, mostra o menu se houver itens para exibir.

    const MyBubble implements IFocusableNode, IContextMenu {
      ...
      showContextMenu(menuOpenEvent) {
        // Get the items from the context menu registry
        const scope = {focusedNode: this};
        const items = Blockly.ContextMenuRegistry.registry.getContextMenuOptions(scope, menuOpenEvent);
    
        // Return early if there are no items available
        if (!items.length) return;
    
        // Show the menu at the same location on screen as this component
        // The location is in pixel coordinates, so translate from workspace coordinates
        const location = Blockly.utils.svgMath.wsToScreenCoordinates(new Coordinate(this.x, this.y));
    
        // Show the context menu
        Blockly.ContextMenu.show(menuOpenEvent, items, this.workspace.RTL, this.workspace, location);
      }
    }
    
  3. Adicione um manipulador de eventos que chame showContextMenu quando o usuário clicar com o botão direito do mouse no componente. O plug-in de navegação por teclado oferece um manipulador de eventos que chama showContextMenu quando o usuário pressiona Ctrl+Enter (Windows) ou Command+Enter (Mac).

  4. Adicione modelos ao registro dos itens do menu de contexto.