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

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

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

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

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

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

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

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

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

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

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

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

ขอบเขต

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

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

ระบบจะส่งออบเจ็กต์ 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'

ค่า คำอธิบาย รูปภาพ
'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. เพิ่มเทมเพลตลงในรีจิสทรีสำหรับรายการในเมนูบริบท