เมนูตามบริบท

เมนูบริบทมีรายการการดำเนินการที่ผู้ใช้สามารถทำกับคอมโพเนนต์ได้ เช่น พื้นที่ทำงาน บล็อก หรือความคิดเห็นในพื้นที่ทำงาน เมนูตามบริบทจะแสดงขึ้น เมื่อคลิกขวาหรือกดค้างบนอุปกรณ์ระบบสัมผัส หากคุณใช้ปลั๊กอิน @blockly/keyboard-navigation ระบบจะแสดงปลั๊กอินพร้อมแป้นพิมพ์ลัด ซึ่งค่าเริ่มต้นคือ Ctrl+Enter ใน Windows หรือ Command+Enter ใน Mac

เมนูตามบริบทเริ่มต้นสำหรับบล็อก

เมนูบริบทเป็นที่ที่เหมาะสําหรับการเพิ่มการกระทําที่ผู้ใช้ทําไม่บ่อยนัก เช่น การดาวน์โหลดภาพหน้าจอ หากคิดว่าการดำเนินการจะ ใช้กันโดยทั่วไป คุณอาจต้องการสร้างวิธีเรียกใช้ที่ค้นพบได้ง่ายขึ้น

พื้นที่ทำงาน บล็อก ความคิดเห็นในพื้นที่ทำงาน บับเบิล และการเชื่อมต่อรองรับเมนูตามบริบท คุณยังใช้กับคอมโพเนนต์ที่กำหนดเองได้ด้วย Blockly มีเมนูบริบทมาตรฐาน ที่คุณปรับแต่งได้ นอกจากนี้ คุณยังปรับแต่งเมนูตามบริบทใน พื้นที่ทำงานและบล็อกได้ตามพื้นที่ทำงานหรือบล็อก

วิธีการทำงานของเมนูตามบริบท

Blockly มีรีจิสทรีที่มีเทมเพลตสำหรับรายการเมนูที่เป็นไปได้ทั้งหมด เทมเพลตแต่ละรายการจะอธิบายวิธีสร้างรายการเดียวในเมนูตามบริบท เมื่อ ผู้ใช้เรียกใช้เมนูตามบริบทในคอมโพเนนต์ คอมโพเนนต์จะทำดังนี้

  1. ขอให้รีจิสทรีสร้างอาร์เรย์ของรายการเมนูที่ใช้กับคอมโพเนนต์ รีจิสทรีจะถามแต่ละเทมเพลตว่าเทมเพลตนั้นใช้กับคอมโพเนนต์ได้หรือไม่ และหากใช้ได้ ก็จะเพิ่มรายการเมนูที่เกี่ยวข้องลงในอาร์เรย์

  2. หากคอมโพเนนต์เป็นพื้นที่ทำงานหรือบล็อก ระบบจะตรวจสอบว่าพื้นที่ทำงานหรือบล็อกที่เรียกใช้เมนูมีฟังก์ชันสำหรับ ปรับแต่งเมนูตามบริบทหรือไม่ หากเป็นเช่นนั้น ฟังก์ชันจะส่งอาร์เรย์ไปยังฟังก์ชัน ซึ่งสามารถเพิ่ม ลบ หรือแก้ไของค์ประกอบของอาร์เรย์ได้

  3. แสดงเมนูตามบริบทโดยใช้อาร์เรย์ของรายการเมนูตามบริบท (อาจมีการแก้ไข)

Blockly กำหนดชุดเทมเพลตมาตรฐานสำหรับเมนูบริบทของ พื้นที่ทำงาน บล็อก และความคิดเห็นในพื้นที่ทำงาน โดยจะโหลดเทมเพลตสำหรับ พื้นที่ทำงานและบล็อกไว้ล่วงหน้าในรีจิสทรี หากต้องการใช้เทมเพลตสำหรับ ความคิดเห็นในพื้นที่ทำงาน คุณต้องโหลดลงในรีจิสทรีด้วยตนเอง

ดูข้อมูลเกี่ยวกับวิธีเพิ่ม ลบ และแก้ไขเทมเพลตในรีจิสทรีได้ที่ ปรับแต่งรีจิสทรี

ขอบเขต

เมนูตามบริบทจะได้รับการติดตั้งใช้งานโดยคอมโพเนนต์ประเภทต่างๆ ซึ่งรวมถึง พื้นที่ทำงาน ความคิดเห็นในพื้นที่ทำงาน การเชื่อมต่อ บล็อก บับเบิล และ คอมโพเนนต์ที่กำหนดเองของคุณเอง เมนูตามบริบทสำหรับคอมโพเนนต์แต่ละประเภทอาจมีรายการต่างๆ และรายการอาจทำงานแตกต่างกันไปตามประเภทคอมโพเนนต์ ดังนั้น ระบบเมนูตามบริบทจึงต้องทราบว่ามีการเรียกใช้คอมโพเนนต์ใด

เพื่อแก้ไขปัญหานี้ รีจิสทรีจึงใช้ออบเจ็กต์ Scope คอมโพเนนต์ที่เรียกใช้เมนูบริบทจะจัดเก็บไว้ในพร็อพเพอร์ตี้ focusedNode เป็นออบเจ็กต์ ที่ใช้ IFocusableNode (IFocusableNode ดำเนินการโดยคอมโพเนนต์ทั้งหมดที่ผู้ใช้สามารถโฟกัสได้ รวมถึงคอมโพเนนต์ที่ใช้เมนูบริบท ดูข้อมูลเพิ่มเติมได้ที่ระบบ โฟกัส)

ระบบจะส่งออบเจ็กต์ Scope ไปยังฟังก์ชันหลายรายการในเทมเพลต ในฟังก์ชันใดก็ตามที่รับออบเจ็กต์ Scope คุณสามารถตัดสินใจว่าจะทำอะไรโดยอิงตามประเภท ของออบเจ็กต์ในพร็อพเพอร์ตี้ focusedNode เช่น คุณสามารถตรวจสอบได้ว่าคอมโพเนนต์เป็นบล็อกที่มีลักษณะดังนี้หรือไม่

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

ออบเจ็กต์ Scope มีพร็อพเพอร์ตี้อื่นๆ ที่ไม่บังคับซึ่งเราไม่แนะนำให้ใช้แล้ว แต่ยังคงตั้งค่าได้

  • block จะตั้งค่าก็ต่อเมื่อคอมโพเนนต์ที่มีเมนูแสดงเป็น BlockSvg
  • ระบบจะตั้งค่า workspace ก็ต่อเมื่อคอมโพเนนต์เป็น WorkspaceSvg
  • ระบบจะตั้งค่า comment ก็ต่อเมื่อคอมโพเนนต์เป็น RenderedWorkspaceComment

พร็อพเพอร์ตี้เหล่านี้ไม่ได้ครอบคลุมคอมโพเนนต์ทุกประเภทที่อาจมีเมนูบริบท ดังนั้นคุณควรใช้พร็อพเพอร์ตี้ focusedNode

ประเภท RegistryItem

เทมเพลตมีประเภท ContextMenuRegistry.RegistryItem ซึ่งมีพร็อพเพอร์ตี้ต่อไปนี้ โปรดทราบว่าพร็อพเพอร์ตี้ preconditionFn, displayText และ callback จะเป็นข้อมูลแยกกันกับพร็อพเพอร์ตี้ separator

รหัส

พร็อพเพอร์ตี้ id ควรเป็นสตริงที่ไม่ซ้ำซึ่งระบุว่ารายการในเมนูบริบททำอะไร

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

ฟังก์ชันเงื่อนไขเบื้องต้น

คุณใช้ preconditionFn เพื่อจำกัดเวลาและวิธีแสดงรายการในเมนูตามบริบท ได้

โดยควรแสดงผลสตริงชุดใดชุดหนึ่งต่อไปนี้ 'enabled', 'disabled' หรือ 'hidden'

ค่า คำอธิบาย รูปภาพ
เปิดใช้อยู่ แสดงว่า รายการใช้งานอยู่ ตัวเลือกที่เปิดใช้
ปิดอยู่ แสดงว่า รายการไม่ได้ ใช้งานอยู่ ตัวเลือกที่ปิดใช้
ซ่อน ซ่อนรายการ

นอกจากนี้ preconditionFn ยังส่ง Scope ให้ด้วย ซึ่งคุณสามารถใช้เพื่อ ระบุประเภทของคอมโพเนนต์ที่เปิดเมนู และสถานะของคอมโพเนนต์นั้น ได้

ตัวอย่างเช่น คุณอาจต้องการให้รายการปรากฏเฉพาะในบล็อก และเฉพาะเมื่อบล็อกเหล่านั้นอยู่ในสถานะที่เฉพาะเจาะจงเท่านั้น

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

ข้อความที่แสดง

displayText คือสิ่งที่ควรแสดงต่อผู้ใช้เป็นส่วนหนึ่งของรายการเมนู ข้อความที่แสดงอาจเป็นสตริง, HTML หรือฟังก์ชันที่แสดงผลเป็นสตริงหรือ HTML

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

หากต้องการแสดงคำแปลจาก Blockly.Msg คุณต้องใช้ฟังก์ชัน หากพยายามกำหนดค่าโดยตรง ระบบอาจโหลดข้อความไม่ได้และคุณจะได้รับค่าเป็น undefined แทน

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

หากใช้ฟังก์ชัน ระบบจะส่งค่า Scope ไปให้ด้วย คุณใช้ พารามิเตอร์นี้เพื่อเพิ่มข้อมูลเกี่ยวกับองค์ประกอบลงในข้อความที่แสดงได้

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

น้ำหนัก

weight จะกำหนดลำดับการแสดงรายการในเมนูตามบริบท ค่าบวกที่มากกว่าจะแสดงในรายการต่ำกว่าค่าบวกที่น้อยกว่า (คุณอาจจินตนาการได้ว่ารายการที่มีน้ำหนักสูงกว่าจะ "หนักกว่า" จึงจมลงไปที่ด้านล่าง)

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

น้ำหนักสำหรับรายการในเมนูตามบริบทในตัวจะเรียงตามลำดับจากน้อยไปมาก โดยเริ่มที่ 1 และเพิ่มขึ้นทีละ 1

ฟังก์ชัน Callback

พร็อพเพอร์ตี้ callback เป็นฟังก์ชันที่ดำเนินการกับรายการในเมนูบริบท โดยจะส่งพารามิเตอร์หลายรายการ ดังนี้

  • scope: ออบเจ็กต์ Scope ที่ให้การอ้างอิงถึงคอมโพเนนต์ที่มีเมนูเปิดอยู่
  • menuOpenEvent: Event ที่ทริกเกอร์การเปิดเมนูตามบริบท ซึ่งอาจเป็น PointerEvent หรือ KeyboardEvent ขึ้นอยู่กับวิธีที่ผู้ใช้เปิดเมนู
  • menuSelectEvent: Event ที่เลือกรายการเมนูตามบริบทนี้จากเมนู ซึ่งอาจเป็น PointerEvent หรือ KeyboardEvent ขึ้นอยู่กับวิธีที่ผู้ใช้เลือกรายการ
  • location: Coordinate ในพิกเซลพิกัด ที่เปิดเมนู ซึ่งช่วยให้คุณสร้างบล็อกใหม่ที่ตำแหน่งคลิกได้ เป็นต้น
const collapseTemplate = {
  // ...
  callback: (scope, menuOpenEvent, menuSelectEvent, location) => {
    if (scope.focusedNode instanceof Blockly.BlockSvg) {
      scope.focusedNode.collapse();
    }
  },
}

คุณใช้ scope เพื่อออกแบบเทมเพลตที่ทำงานแตกต่างกันได้โดยขึ้นอยู่กับ คอมโพเนนต์ที่เปิดเทมเพลต

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 พร็อพเพอร์ตี้จะวาดเส้นในเมนูตามบริบท

เทมเพลตที่มีพร็อพเพอร์ตี้ separator จะมีพร็อพเพอร์ตี้ preconditionFn, displayText หรือ callback ไม่ได้ และจะกำหนดขอบเขตได้ด้วยพร็อพเพอร์ตี้ scopeType เท่านั้น ข้อจำกัดหลังหมายความว่าใช้ได้เฉพาะใน เมนูบริบทสำหรับพื้นที่ทำงาน บล็อก และความคิดเห็นในพื้นที่ทำงาน

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

คุณต้องมีเทมเพลตที่แตกต่างกันสำหรับตัวคั่นแต่ละตัวในเมนูบริบท ใช้พร็อพเพอร์ตี้ weight เพื่อจัดตำแหน่งตัวคั่นแต่ละตัว

ประเภทขอบเขต

เลิกใช้งานพร็อพเพอร์ตี้ scopeType แล้ว ก่อนหน้านี้ใช้เพื่อพิจารณาว่าควรแสดงรายการเมนูในเมนูตามบริบทสำหรับบล็อก ความคิดเห็นในพื้นที่ทำงาน หรือพื้นที่ทำงานหรือไม่ เนื่องจากเปิดเมนูตามบริบทในคอมโพเนนต์อื่นๆ ได้ พร็อพเพอร์ตี้ scopeType จึงจำกัดมากเกินไป แต่คุณควรใช้ preconditionFn เพื่อแสดงหรือซ่อนตัวเลือกสำหรับคอมโพเนนต์ที่เกี่ยวข้อง

หากคุณมีเทมเพลตเมนูตามบริบทที่ใช้ scopeType อยู่ Blockly จะ แสดงรายการเฉพาะคอมโพเนนต์ที่เหมาะสมต่อไป

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

ปรับแต่งรีจิสทรี

คุณสามารถเพิ่ม ลบ หรือแก้ไขเทมเพลตในรีจิสทรีได้ คุณดูเทมเพลตเริ่มต้นได้ใน contextmenu_items.ts

เพิ่มเทมเพลต

คุณเพิ่มเทมเพลตลงในรีจิสทรีได้โดยการลงทะเบียน คุณควรทำเช่นนี้ เมื่อโหลดหน้าเว็บ ซึ่งอาจเกิดขึ้นก่อนหรือหลังจากที่คุณแทรกพื้นที่ทำงาน

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

ลบเทมเพลต

คุณนำเทมเพลตออกจากรีจิสทรีได้โดยยกเลิกการลงทะเบียนตามรหัส

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

แก้ไขเทมเพลต

คุณแก้ไขเทมเพลตที่มีอยู่ได้โดยรับเทมเพลตจากรีจิสทรี แล้วแก้ไขในตำแหน่ง

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

ปิดใช้เมนูตามบริบทของฟีเจอร์บล็อก

โดยค่าเริ่มต้น บล็อกจะมีเมนูตามบริบทที่ช่วยให้ผู้ใช้ทำสิ่งต่างๆ ได้ เช่น เพิ่มความคิดเห็นในบล็อกหรือทำซ้ำบล็อก

คุณปิดใช้เมนูตามบริบทของบล็อกแต่ละรายการได้โดยทำดังนี้

block.contextMenu = false;

ในคำจำกัดความ JSON ของประเภทบล็อก ให้ใช้คีย์ enableContextMenu

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

ปรับแต่งเมนูตามบริบทต่อประเภทบล็อกหรือพื้นที่ทำงาน

หลังจากที่ Blockly สร้างอาร์เรย์ของรายการเมนูบริบทแล้ว คุณจะปรับแต่งรายการดังกล่าวสำหรับบล็อกหรือพื้นที่ทำงานแต่ละรายการได้ โดยตั้งค่า BlockSvg.customContextMenu หรือ WorkspaceSvg.configureContextMenu เป็นฟังก์ชันที่ แก้ไขอาร์เรย์ในตำแหน่ง

ออบเจ็กต์ในอาร์เรย์ที่ส่งไปยังบล็อกมีประเภท ContextMenuOption หรือใช้ส่วนติดต่อ LegacyContextMenuOption ออบเจ็กต์ที่ส่งไปยัง พื้นที่ทํางานมีประเภท ContextMenuOption Blockly ใช้พร็อพเพอร์ตี้ต่อไปนี้ จากออบเจ็กต์เหล่านี้

  • text: ข้อความที่แสดง
  • enabled: หาก false ให้แสดงรายการด้วยข้อความสีเทา
  • callback: ฟังก์ชันที่จะเรียกใช้เมื่อคลิกรายการ
  • separator: รายการนี้เป็นตัวคั่น ใช้ร่วมกับพร็อพเพอร์ตี้อีก 3 รายการไม่ได้

ดูเอกสารอ้างอิงสำหรับประเภทพร็อพเพอร์ตี้และลายเซ็นฟังก์ชัน

ตัวอย่างเช่น ฟังก์ชันต่อไปนี้จะเพิ่มรายการ Hello, World! ลงในเมนูตามบริบทของพื้นที่ทํางาน

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

แสดงเมนูตามบริบทในออบเจ็กต์ที่กำหนดเอง

คุณสามารถทำให้เมนูบริบทปรากฏสำหรับคอมโพเนนต์ที่กำหนดเองได้โดยทำตามขั้นตอนต่อไปนี้

  1. ใช้ IFocusableNode หรือขยายคลาสที่ใช้ IFocusableNode อินเทอร์เฟซนี้ใช้ในเมนูบริบทของระบบเพื่อระบุคอมโพเนนต์ นอกจากนี้ยังช่วยให้ผู้ใช้ ไปยังคอมโพเนนต์ของคุณได้โดยใช้ปลั๊กอินการนำทางด้วยแป้นพิมพ์
  2. ใช้ IContextMenu ซึ่งมีฟังก์ชัน showContextMenu ฟังก์ชันนี้จะรับรายการเมนูตามบริบทจากรีจิสทรี คำนวณตำแหน่งบนหน้าจอที่จะแสดงเมนู และแสดงเมนูในที่สุดหากมีรายการที่จะแสดง

    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. เพิ่มตัวแฮนเดิลเหตุการณ์ที่เรียกใช้ showContextMenu เมื่อผู้ใช้คลิกขวาที่คอมโพเนนต์ โปรดทราบว่าปลั๊กอินการนำทางด้วยคีย์บอร์ดมีตัวแฮนเดิลเหตุการณ์ที่เรียกใช้ showContextMenu เมื่อผู้ใช้กด Ctrl+Enter (Windows) หรือ Command+Enter (Mac)

  4. เพิ่มเทมเพลตลงในรีจิสทรีสำหรับรายการในเมนูบริบท