@@ -1231,13 +1231,13 @@ export class Navigation {
12311231 */
12321232 handleEnterForWS ( workspace : Blockly . WorkspaceSvg ) {
12331233 const cursor = workspace . getCursor ( ) ;
1234- if ( ! cursor ) {
1235- return ;
1236- }
1234+ if ( ! cursor ) return ;
12371235 const curNode = cursor . getCurNode ( ) ;
12381236 const nodeType = curNode . getType ( ) ;
12391237 if ( nodeType == Blockly . ASTNode . types . FIELD ) {
12401238 ( curNode . getLocation ( ) as Blockly . Field ) . showEditor ( ) ;
1239+ } else if ( nodeType == Blockly . ASTNode . types . BLOCK ) {
1240+ this . openActionMenu ( curNode ) ;
12411241 } else if (
12421242 curNode . isConnection ( ) ||
12431243 nodeType == Blockly . ASTNode . types . WORKSPACE
@@ -1248,13 +1248,63 @@ export class Navigation {
12481248 } else {
12491249 this . focusFlyout ( workspace ) ;
12501250 }
1251- } else if ( nodeType == Blockly . ASTNode . types . BLOCK ) {
1252- this . warn ( 'Cannot mark a block.' ) ;
12531251 } else if ( nodeType == Blockly . ASTNode . types . STACK ) {
12541252 this . warn ( 'Cannot mark a stack.' ) ;
12551253 }
12561254 }
12571255
1256+ /**
1257+ * Show the action menu for a given node.
1258+ *
1259+ * The action menu will contain entries for relevant actions for the
1260+ * node's location. If the location is a block, this will include
1261+ * the contents of the block's context menu (if any).
1262+ */
1263+ openActionMenu ( node : Blockly . ASTNode ) {
1264+ const fakeEvent = fakeEventForNode ( node ) ;
1265+ ( node . getLocation ( ) as Blockly . BlockSvg ) . showContextMenu ( fakeEvent ) ;
1266+
1267+ let menuOptions : Array <
1268+ | Blockly . ContextMenuRegistry . ContextMenuOption
1269+ | Blockly . ContextMenuRegistry . LegacyContextMenuOption
1270+ > | null = null ;
1271+ let rtl : boolean ;
1272+ let workspace : Blockly . WorkspaceSvg ;
1273+
1274+ const nodeType = node . getType ( ) ;
1275+ switch ( nodeType ) {
1276+ case Blockly . ASTNode . types . BLOCK :
1277+ const block = node . getLocation ( ) as Blockly . BlockSvg ;
1278+ workspace = block . workspace as Blockly . WorkspaceSvg ;
1279+ rtl = block . RTL ;
1280+
1281+ // Reimplement BlockSvg.prototype.generateContextMenu as that
1282+ // method is protected.
1283+ if ( ! workspace . options . readOnly && ! block . contextMenu ) {
1284+ menuOptions =
1285+ Blockly . ContextMenuRegistry . registry . getContextMenuOptions (
1286+ Blockly . ContextMenuRegistry . ScopeType . BLOCK ,
1287+ { block} ,
1288+ ) ;
1289+
1290+ // Allow the block to add or modify menuOptions.
1291+ if ( block . customContextMenu ) {
1292+ block . customContextMenu ( menuOptions ) ;
1293+ }
1294+ }
1295+ // End reimplement.
1296+ break ;
1297+ default :
1298+ throw new TypeError (
1299+ `unable to show action menu for ASTNode of type ${ nodeType } ` ,
1300+ ) ;
1301+ }
1302+
1303+ if ( ! menuOptions || ! menuOptions . length ) return ;
1304+
1305+ Blockly . ContextMenu . show ( fakeEvent , menuOptions , rtl , workspace ) ;
1306+ }
1307+
12581308 /**
12591309 * Pastes the copied block to the marked location if possible or
12601310 * onto the workspace otherwise.
@@ -1333,3 +1383,32 @@ export class Navigation {
13331383 }
13341384 }
13351385}
1386+
1387+ /**
1388+ * Create a fake PointerEvent for opening the action menu for the
1389+ * given ASTNode.
1390+ *
1391+ * Currently only works for block nodes.
1392+ *
1393+ * @param node The node to open the action menu for.
1394+ * @returns A synthetic pointerdown PointerEvent.
1395+ */
1396+ function fakeEventForNode ( node : Blockly . ASTNode ) : PointerEvent {
1397+ if ( node . getType ( ) !== Blockly . ASTNode . types . BLOCK ) {
1398+ throw new TypeError ( 'can only create PointerEvents for BLOCK nodes' ) ;
1399+ }
1400+
1401+ // Get the location of the top-left corner of the block in
1402+ // screen coordinates.
1403+ const block = node . getLocation ( ) as Blockly . BlockSvg ;
1404+ const coords = Blockly . utils . svgMath . wsToScreenCoordinates (
1405+ block . workspace ,
1406+ block . getRelativeToSurfaceXY ( ) ,
1407+ ) ;
1408+
1409+ // Create a fake event for the action menu code to work from.
1410+ return new PointerEvent ( 'pointerdown' , {
1411+ clientX : coords . x ,
1412+ clientY : coords . y ,
1413+ } ) ;
1414+ }
0 commit comments