Menu kontekstowe

Menu kontekstowe zawiera listę działań, które użytkownik może wykonać na komponencie, takim jak obszar roboczy, blok lub komentarz do obszaru roboczego. Menu kontekstowe jest wyświetlane w odpowiedzi na kliknięcie prawym przyciskiem myszy lub przytrzymanie na urządzeniu dotykowym. Jeśli używasz @blockly/keyboard-navigationwtyczki, jest ona również wyświetlana ze skrótem klawiszowym, który domyślnie to Ctrl+Enter w systemie Windows lub Command+Enter na komputerze Mac.

Domyślne menu kontekstowe bloku

Menu kontekstowe to dobre miejsce na dodanie działań, które użytkownik wykonuje rzadko, np. pobieranie zrzutu ekranu. Jeśli uważasz, że akcja będzie częściej używana, możesz utworzyć łatwiejszy sposób jej wywoływania.

Menu kontekstowe są obsługiwane przez obszary robocze, bloki, komentarze do obszaru roboczego, dymki i połączenia. Możesz też wdrożyć je we własnych komponentach niestandardowych. Blockly udostępnia standardowe menu kontekstowe, które możesz dostosować. Możesz też dostosowywać menu kontekstowe w obszarach roboczych i blokach w przypadku poszczególnych obszarów roboczych lub bloków.

Jak działają menu kontekstowe

Blockly ma rejestr, który zawiera szablony wszystkich możliwych elementów menu. Każdy szablon opisuje, jak utworzyć pojedynczy element w menu kontekstowym. Gdy użytkownik wywoła menu kontekstowe komponentu, komponent:

  1. Prosi rejestr o utworzenie tablicy pozycji menu, które mają zastosowanie do komponentu. Rejestr pyta każdy szablon, czy ma zastosowanie do komponentu, a jeśli tak, dodaje odpowiednią pozycję menu do tablicy.

  2. Jeśli komponentem jest obszar roboczy lub blok, sprawdza, czy dany obszar roboczy lub blok, w którym wywołano menu, ma funkcję dostosowywania menu kontekstowego. Jeśli tak, przekazuje tablicę do funkcji, która może dodawać, usuwać lub modyfikować elementy tablicy.

  3. Wyświetla menu kontekstowe przy użyciu (prawdopodobnie zmodyfikowanej) tablicy elementów menu kontekstowego.

Blockly definiuje standardowy zestaw szablonów menu kontekstowych obszarów roboczych, bloków i komentarzy do obszaru roboczego. Wstępnie wczytuje szablony obszarów roboczych i bloków do rejestru. Jeśli chcesz używać szablonów do komentarzy w obszarze roboczym, musisz samodzielnie załadować je do rejestru.

Informacje o tym, jak dodawać, usuwać i modyfikować szablony w rejestrze, znajdziesz w artykule Dostosowywanie rejestru.

Zakres

Menu kontekstowe są implementowane przez różne typy komponentów, w tym obszary robocze, komentarze do obszarów roboczych, połączenia, bloki, dymki i własne komponenty niestandardowe. Menu kontekstowe poszczególnych typów komponentów mogą zawierać różne elementy, a elementy mogą zachowywać się inaczej w zależności od typu komponentu. Dlatego system menu kontekstowego musi wiedzieć, na którym komponencie zostało ono wywołane.

W tym celu rejestr używa obiektu Scope. Komponent, w którym wywołano menu kontekstowe, jest przechowywany we właściwości focusedNode jako obiekt implementujący interfejs IFocusableNode. (IFocusableNode jest implementowany przez wszystkie komponenty, na których użytkownicy mogą się skupić, w tym te, które implementują menu kontekstowe).

Obiekt Scope jest przekazywany do kilku funkcji w szablonie. W dowolnej funkcji, która otrzymuje obiekt Scope, możesz zdecydować, co zrobić na podstawie typu obiektu we właściwości focusedNode. Możesz na przykład sprawdzić, czy komponent jest blokiem, za pomocą tego kodu:

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

Obiekt Scope ma inne właściwości opcjonalne, których nie zalecamy już używać, ale nadal można je ustawić:

  • block jest ustawiony tylko wtedy, gdy komponent, którego menu jest wyświetlane, jest elementem BlockSvg.
  • workspace jest ustawiony tylko wtedy, gdy komponent jest elementem WorkspaceSvg.
  • comment jest ustawiony tylko wtedy, gdy komponent jest elementem RenderedWorkspaceComment.

Te właściwości nie obejmują wszystkich typów komponentów, które mogą mieć menu kontekstowe, dlatego lepiej używać właściwości focusedNode.

Typ RegistryItem

Szablony mają typ ContextMenuRegistry.RegistryItem, który zawiera te właściwości: Właściwości preconditionFn, displayTextcallback wykluczają się wzajemnie z właściwością separator.

Identyfikator

Właściwość id powinna być unikalnym ciągiem znaków, który wskazuje, co robi element menu kontekstowego.

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

Funkcja warunku wstępnego

Za pomocą właściwości preconditionFn możesz ograniczyć, kiedy i w jaki sposób ma być wyświetlana pozycja menu kontekstowego.

Powinna zwracać jeden z tych ciągów znaków: 'enabled', 'disabled' lub 'hidden'.

Wartość Opis Obraz
włączone Wskazuje, że element jest aktywny. Włączona opcja
wyłączono Wskazuje, że produkt jest nieaktywny. Wyłączona opcja
ukryta Ukrywa element.

Do funkcji preconditionFn przekazywany jest też obiekt Scope, którego możesz użyć do określenia typu komponentu, w którym zostało otwarte menu, oraz stanu tego komponentu.

Możesz na przykład chcieć, aby element pojawiał się tylko w przypadku bloków i tylko wtedy, gdy te bloki są w określonym stanie:

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';
  },
  // ...
}

Tekst wyświetlany

Znak displayText powinien być wyświetlany użytkownikowi jako część pozycji menu. Tekst wyświetlany może być ciągiem znaków, kodem HTML lub funkcją, która zwraca ciąg znaków lub kod HTML.

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

Jeśli chcesz wyświetlić tłumaczenie z Blockly.Msg, musisz użyć funkcji. Jeśli spróbujesz przypisać wartość bezpośrednio, wiadomości mogą się nie załadować i zamiast nich otrzymasz wartość undefined.

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

Jeśli używasz funkcji, przekazywana jest do niej również wartość Scope. Możesz użyć tego parametru, aby dodać do tekstu wyświetlanego informacje o elemencie.

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 '';
  },
  // ...
}

Waga

weight określa kolejność wyświetlania elementów menu kontekstowego. Bardziej pozytywne wartości są wyświetlane niżej na liście niż mniej pozytywne. (Możesz sobie wyobrazić, że elementy o większej wadze są „cięższe”, więc opadają na dno).

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

Wagi wbudowanych elementów menu kontekstowego są podawane w kolejności rosnącej, zaczynając od 1 i zwiększając się o 1.

Wywołanie zwrotne

Właściwość callback to funkcja, która wykonuje działanie elementu menu kontekstowego. Przekazywanych jest do niej kilka parametrów:

  • scope: obiekt Scope, który zawiera odniesienie do komponentu, którego menu jest otwarte.
  • menuOpenEvent: Event, który spowodował otwarcie menu kontekstowego. Może to być PointerEvent lub KeyboardEvent, w zależności od tego, jak użytkownik otworzył menu.
  • menuSelectEvent: Event, który wybrał ten konkretny element menu kontekstowego. Może to być ikona PointerEvent lub KeyboardEvent, w zależności od tego, jak użytkownik wybrał produkt.
  • location: Coordinate w współrzędnych pikseli, w których otwarto menu. Dzięki temu możesz na przykład utworzyć nowy blok w miejscu kliknięcia.
const collapseTemplate = {
  // ...
  callback: (scope, menuOpenEvent, menuSelectEvent, location) => {
    if (scope.focusedNode instanceof Blockly.BlockSvg) {
      scope.focusedNode.collapse();
    }
  },
}

Za pomocą symbolu scope możesz projektować szablony, które działają inaczej w zależności od komponentu, w którym zostały otwarte:

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);
    }
  }
}

Separator

Właściwość separator rysuje linię w menu kontekstowym.

Szablony z właściwością separator nie mogą mieć właściwości preconditionFn, displayText ani callback i mogą być ograniczone tylko za pomocą właściwości scopeType. To drugie ograniczenie oznacza, że można ich używać tylko w menu kontekstowych obszarów roboczych, bloków i komentarzy do obszarów roboczych.

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

Dla każdego separatora w menu kontekstowym potrzebujesz innego szablonu. Użyj właściwości weight, aby umieścić każdy separator.

Typ zakresu

Właściwość scopeType została wycofana. Wcześniej służyło do określania, czy element menu powinien być wyświetlany w menu kontekstowym bloku, komentarza w obszarze roboczym lub obszaru roboczego. Menu kontekstowe można otwierać w innych komponentach, więc właściwość scopeType jest zbyt restrykcyjna. Zamiast tego użyj elementu preconditionFn, aby wyświetlić lub ukryć opcję dla odpowiednich komponentów.

Jeśli masz szablony menu kontekstowego, które używają scopeType, Blockly będzie nadal wyświetlać element tylko w odpowiednim komponencie.

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

Dostosowywanie rejestru

W rejestrze możesz dodawać, usuwać i modyfikować szablony. Domyślne szablony znajdziesz w regionie contextmenu_items.ts.

Dodawanie szablonu

Możesz dodać szablon do rejestru, rejestrując go. Należy to zrobić raz podczas wczytywania strony. Może to nastąpić przed wstrzyknięciem obszaru roboczego lub po nim.

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

Usuwanie szablonu

Możesz usunąć szablon z rejestru, wyrejestrowując go według identyfikatora.

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

Modyfikowanie szablonu

Możesz zmodyfikować istniejący szablon, pobierając go z rejestru, a następnie wprowadzając zmiany w miejscu jego przechowywania.

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

Wyłącz blokowanie menu kontekstowych

Domyślnie bloki mają menu kontekstowe, które umożliwia użytkownikom wykonywanie takich czynności jak dodawanie komentarzy do bloków czy ich duplikowanie.

Aby wyłączyć menu kontekstowe poszczególnych bloków:

block.contextMenu = false;

W definicji JSON typu bloku użyj klucza enableContextMenu:

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

Dostosowywanie menu kontekstowych według typu bloku lub obszaru roboczego

Gdy Blockly wygeneruje tablicę elementów menu kontekstowego, możesz ją dostosować do poszczególnych bloków lub obszarów roboczych. Aby to zrobić, ustaw BlockSvg.customContextMenu lub WorkspaceSvg.configureContextMenu na funkcję, która modyfikuje tablicę w miejscu.

Obiekty w tablicy przekazywanej do bloków mają typ ContextMenuOption lub implementują interfejs LegacyContextMenuOption. Obiekty przekazywane do obszarów roboczych mają typ ContextMenuOption. Blockly używa tych właściwości z tych obiektów:

  • text: tekst wyświetlany.
  • enabled: Jeśli false, wyświetl element z szarym tekstem.
  • callback: funkcja, która ma zostać wywołana po kliknięciu elementu.
  • separator: element jest separatorem. Wzajemnie wykluczające się z pozostałymi 3 właściwościami.

Informacje o typach właściwości i sygnaturach funkcji znajdziesz w dokumentacji.

Oto na przykład funkcja, która dodaje element Hello, World! do menu kontekstowego obszaru roboczego:

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);
}

Wyświetlanie menu kontekstowego w przypadku obiektu niestandardowego

Aby menu kontekstowe pojawiało się w przypadku komponentów niestandardowych, wykonaj te czynności:

  1. Wdróż IFocusableNode. Ten interfejs jest używany w systemie menu kontekstowego do identyfikowania komponentu. Umożliwia też użytkownikom przechodzenie do komponentu za pomocą wtyczki do nawigacji klawiaturą.
  2. Zaimplementuj IContextMenu, który zawiera funkcję showContextMenu. Ta funkcja pobiera elementy menu kontekstowego z rejestru, oblicza miejsce na ekranie, w którym ma się wyświetlić menu, i w końcu wyświetla menu, jeśli są w nim jakieś elementy.

    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. Dodaj procedurę obsługi zdarzeń, która wywołuje funkcję showContextMenu, gdy użytkownik kliknie komponent prawym przyciskiem myszy. Pamiętaj, że wtyczka do nawigacji za pomocą klawiatury udostępnia procedurę obsługi zdarzeń, która wywołuje funkcję showContextMenu, gdy użytkownik naciśnie klawisz Ctrl+Enter (Windows) lub Command+Enter (Mac).

  4. Dodaj szablony do rejestru dla elementów menu kontekstowego.