mirror of
https://github.com/google/blockly.git
synced 2026-01-09 01:50:11 +01:00
Use Context Menu registry for block-level menu options (#4032)
Use registry for block-level context menu items.
This commit is contained in:
committed by
GitHub
parent
40efb59a34
commit
e4bbd451a3
@@ -17,6 +17,7 @@ goog.require('Blockly.Block');
|
||||
goog.require('Blockly.blockAnimations');
|
||||
goog.require('Blockly.blockRendering.IPathObject');
|
||||
goog.require('Blockly.ContextMenu');
|
||||
goog.require('Blockly.ContextMenuRegistry');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.Ui');
|
||||
goog.require('Blockly.Events.BlockMove');
|
||||
@@ -716,87 +717,8 @@ Blockly.BlockSvg.prototype.generateContextMenu = function() {
|
||||
if (this.workspace.options.readOnly || !this.contextMenu) {
|
||||
return null;
|
||||
}
|
||||
// Save the current block in a variable for use in closures.
|
||||
var block = this;
|
||||
var menuOptions = [];
|
||||
|
||||
if (!this.isInFlyout) {
|
||||
if (this.isDeletable() && this.isMovable()) {
|
||||
menuOptions.push(Blockly.ContextMenu.blockDuplicateOption(block));
|
||||
}
|
||||
|
||||
if (this.workspace.options.comments && !this.collapsed_ &&
|
||||
this.isEditable()) {
|
||||
menuOptions.push(Blockly.ContextMenu.blockCommentOption(block));
|
||||
}
|
||||
|
||||
if (this.isMovable()) {
|
||||
if (!this.collapsed_) {
|
||||
// Option to make block inline.
|
||||
for (var i = 1; i < this.inputList.length; i++) {
|
||||
if (this.inputList[i - 1].type != Blockly.NEXT_STATEMENT &&
|
||||
this.inputList[i].type != Blockly.NEXT_STATEMENT) {
|
||||
// Only display this option if there are two value or dummy inputs
|
||||
// next to each other.
|
||||
var inlineOption = {enabled: true};
|
||||
var isInline = this.getInputsInline();
|
||||
inlineOption.text = isInline ?
|
||||
Blockly.Msg['EXTERNAL_INPUTS'] : Blockly.Msg['INLINE_INPUTS'];
|
||||
inlineOption.callback = function() {
|
||||
block.setInputsInline(!isInline);
|
||||
};
|
||||
menuOptions.push(inlineOption);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Option to collapse block
|
||||
if (this.workspace.options.collapse) {
|
||||
var collapseOption = {enabled: true};
|
||||
collapseOption.text = Blockly.Msg['COLLAPSE_BLOCK'];
|
||||
collapseOption.callback = function() {
|
||||
block.setCollapsed(true);
|
||||
};
|
||||
menuOptions.push(collapseOption);
|
||||
}
|
||||
} else {
|
||||
// Option to expand block.
|
||||
if (this.workspace.options.collapse) {
|
||||
var expandOption = {enabled: true};
|
||||
expandOption.text = Blockly.Msg['EXPAND_BLOCK'];
|
||||
expandOption.callback = function() {
|
||||
block.setCollapsed(false);
|
||||
};
|
||||
menuOptions.push(expandOption);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.workspace.options.disable && this.isEditable()) {
|
||||
// Option to disable/enable block.
|
||||
var disableOption = {
|
||||
text: this.isEnabled() ?
|
||||
Blockly.Msg['DISABLE_BLOCK'] : Blockly.Msg['ENABLE_BLOCK'],
|
||||
enabled: !this.getInheritedDisabled(),
|
||||
callback: function() {
|
||||
var group = Blockly.Events.getGroup();
|
||||
if (!group) {
|
||||
Blockly.Events.setGroup(true);
|
||||
}
|
||||
block.setEnabled(!block.isEnabled());
|
||||
if (!group) {
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
menuOptions.push(disableOption);
|
||||
}
|
||||
|
||||
if (this.isDeletable()) {
|
||||
menuOptions.push(Blockly.ContextMenu.blockDeleteOption(block));
|
||||
}
|
||||
}
|
||||
|
||||
menuOptions.push(Blockly.ContextMenu.blockHelpOption(block));
|
||||
var menuOptions = Blockly.ContextMenuRegistry.registry.getContextMenuOptions(
|
||||
Blockly.ContextMenuRegistry.ScopeType.BLOCK, {block: this});
|
||||
|
||||
// Allow the block to add or modify menuOptions.
|
||||
if (this.customContextMenu) {
|
||||
|
||||
@@ -203,98 +203,6 @@ Blockly.ContextMenu.callbackFactory = function(block, xml) {
|
||||
|
||||
// Helper functions for creating context menu options.
|
||||
|
||||
/**
|
||||
* Make a context menu option for deleting the current block.
|
||||
* @param {!Blockly.BlockSvg} block The block where the right-click originated.
|
||||
* @return {!Object} A menu option, containing text, enabled, and a callback.
|
||||
* @package
|
||||
*/
|
||||
Blockly.ContextMenu.blockDeleteOption = function(block) {
|
||||
// Option to delete this block but not blocks lower in the stack.
|
||||
// Count the number of blocks that are nested in this block.
|
||||
var descendantCount = block.getDescendants(false).length;
|
||||
var nextBlock = block.getNextBlock();
|
||||
if (nextBlock) {
|
||||
// Blocks in the current stack would survive this block's deletion.
|
||||
descendantCount -= nextBlock.getDescendants(false).length;
|
||||
}
|
||||
var deleteOption = {
|
||||
text: descendantCount == 1 ? Blockly.Msg['DELETE_BLOCK'] :
|
||||
Blockly.Msg['DELETE_X_BLOCKS'].replace('%1', String(descendantCount)),
|
||||
enabled: true,
|
||||
callback: function() {
|
||||
Blockly.Events.setGroup(true);
|
||||
block.dispose(true, true);
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
};
|
||||
return deleteOption;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a context menu option for showing help for the current block.
|
||||
* @param {!Blockly.BlockSvg} block The block where the right-click originated.
|
||||
* @return {!Object} A menu option, containing text, enabled, and a callback.
|
||||
* @package
|
||||
*/
|
||||
Blockly.ContextMenu.blockHelpOption = function(block) {
|
||||
var url = (typeof block.helpUrl == 'function') ?
|
||||
block.helpUrl() : block.helpUrl;
|
||||
var helpOption = {
|
||||
enabled: !!url,
|
||||
text: Blockly.Msg['HELP'],
|
||||
callback: function() {
|
||||
block.showHelp();
|
||||
}
|
||||
};
|
||||
return helpOption;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a context menu option for duplicating the current block.
|
||||
* @param {!Blockly.BlockSvg} block The block where the right-click originated.
|
||||
* @return {!Object} A menu option, containing text, enabled, and a callback.
|
||||
* @package
|
||||
*/
|
||||
Blockly.ContextMenu.blockDuplicateOption = function(block) {
|
||||
var enabled = block.isDuplicatable();
|
||||
var duplicateOption = {
|
||||
text: Blockly.Msg['DUPLICATE_BLOCK'],
|
||||
enabled: enabled,
|
||||
callback: function() {
|
||||
Blockly.duplicate(block);
|
||||
}
|
||||
};
|
||||
return duplicateOption;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a context menu option for adding or removing comments on the current
|
||||
* block.
|
||||
* @param {!Blockly.BlockSvg} block The block where the right-click originated.
|
||||
* @return {!Object} A menu option, containing text, enabled, and a callback.
|
||||
* @package
|
||||
*/
|
||||
Blockly.ContextMenu.blockCommentOption = function(block) {
|
||||
var commentOption = {
|
||||
enabled: !Blockly.utils.userAgent.IE
|
||||
};
|
||||
// If there's already a comment, add an option to delete it.
|
||||
if (block.getCommentIcon()) {
|
||||
commentOption.text = Blockly.Msg['REMOVE_COMMENT'];
|
||||
commentOption.callback = function() {
|
||||
block.setCommentText(null);
|
||||
};
|
||||
} else {
|
||||
// If there's no comment, add an option to create a comment.
|
||||
commentOption.text = Blockly.Msg['ADD_COMMENT'];
|
||||
commentOption.callback = function() {
|
||||
block.setCommentText('');
|
||||
};
|
||||
}
|
||||
return commentOption;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a context menu option for deleting the current workspace comment.
|
||||
* @param {!Blockly.WorkspaceCommentSvg} comment The workspace comment where the
|
||||
|
||||
@@ -20,65 +20,71 @@ goog.requireType('Blockly.BlockSvg');
|
||||
|
||||
/** Option to undo previous action. */
|
||||
Blockly.ContextMenuItems.registerUndo = function() {
|
||||
var undoOption = {};
|
||||
undoOption.displayText = function() {
|
||||
return Blockly.Msg['UNDO'];
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var undoOption = {
|
||||
displayText: function() {
|
||||
return Blockly.Msg['UNDO'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (scope.workspace.getUndoStack().length > 0) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'disabled';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
scope.workspace.undo(false);
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
|
||||
id: 'undoWorkspace',
|
||||
weight: 0,
|
||||
};
|
||||
undoOption.preconditionFn = function(scope) {
|
||||
if (scope.workspace.undoStack_.length > 0) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'disabled';
|
||||
};
|
||||
undoOption.callback = function(scope) {
|
||||
scope.workspace.undo(false);
|
||||
};
|
||||
undoOption.scopeType = Blockly.ContextMenuRegistry.ScopeType.WORKSPACE;
|
||||
undoOption.id = 'undoWorkspace';
|
||||
undoOption.weight = 0;
|
||||
Blockly.ContextMenuRegistry.registry.register(undoOption);
|
||||
};
|
||||
|
||||
/** Option to redo previous action. */
|
||||
Blockly.ContextMenuItems.registerRedo = function() {
|
||||
var redoOption = {};
|
||||
redoOption.displayText = function() { return Blockly.Msg['REDO']; };
|
||||
redoOption.preconditionFn = function(scope) {
|
||||
if (scope.workspace.redoStack_.length > 0) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'disabled';
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var redoOption = {
|
||||
displayText: function() { return Blockly.Msg['REDO']; },
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (scope.workspace.getRedoStack().length > 0) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'disabled';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
scope.workspace.undo(true);
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
|
||||
id: 'redoWorkspace',
|
||||
weight: 0,
|
||||
};
|
||||
redoOption.callback = function(scope) {
|
||||
scope.workspace.undo(true);
|
||||
};
|
||||
redoOption.scopeType = Blockly.ContextMenuRegistry.ScopeType.WORKSPACE;
|
||||
redoOption.id = 'redoWorkspace';
|
||||
redoOption.weight = 0;
|
||||
Blockly.ContextMenuRegistry.registry.register(redoOption);
|
||||
};
|
||||
|
||||
/** Option to clean up blocks. */
|
||||
Blockly.ContextMenuItems.registerCleanup = function() {
|
||||
var cleanOption = {};
|
||||
cleanOption.displayText = function() {
|
||||
return Blockly.Msg['CLEAN_UP'];
|
||||
};
|
||||
cleanOption.preconditionFn = function(scope) {
|
||||
if (scope.workspace.isMovable()) {
|
||||
if (scope.workspace.getTopBlocks(false).length > 1) {
|
||||
return 'enabled';
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var cleanOption = {
|
||||
displayText: function() {
|
||||
return Blockly.Msg['CLEAN_UP'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (scope.workspace.isMovable()) {
|
||||
if (scope.workspace.getTopBlocks(false).length > 1) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'disabled';
|
||||
}
|
||||
return 'disabled';
|
||||
}
|
||||
return 'hidden';
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
scope.workspace.cleanUp();
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
|
||||
id: 'cleanWorkspace',
|
||||
weight: 0,
|
||||
};
|
||||
cleanOption.callback = function(scope) {
|
||||
scope.workspace.cleanUp();
|
||||
};
|
||||
cleanOption.scopeType = Blockly.ContextMenuRegistry.ScopeType.WORKSPACE;
|
||||
cleanOption.id = 'cleanWorkspace';
|
||||
cleanOption.weight = 0;
|
||||
Blockly.ContextMenuRegistry.registry.register(cleanOption);
|
||||
};
|
||||
|
||||
@@ -103,63 +109,67 @@ Blockly.ContextMenuItems.toggleOption_ = function(shouldCollapse, topBlocks) {
|
||||
|
||||
/** Option to collapse all blocks. */
|
||||
Blockly.ContextMenuItems.registerCollapse = function() {
|
||||
var collapseOption = {};
|
||||
collapseOption.displayText = function() {
|
||||
return Blockly.Msg['COLLAPSE_ALL'];
|
||||
};
|
||||
collapseOption.preconditionFn = function(scope) {
|
||||
if (scope.workspace.options.collapse) {
|
||||
var topBlocks = scope.workspace.getTopBlocks(false);
|
||||
for (var i = 0; i < topBlocks.length; i++) {
|
||||
var block = topBlocks[i];
|
||||
while (block) {
|
||||
if (!block.isCollapsed()) {
|
||||
return 'enabled';
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var collapseOption = {
|
||||
displayText: function() {
|
||||
return Blockly.Msg['COLLAPSE_ALL'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (scope.workspace.options.collapse) {
|
||||
var topBlocks = scope.workspace.getTopBlocks(false);
|
||||
for (var i = 0; i < topBlocks.length; i++) {
|
||||
var block = topBlocks[i];
|
||||
while (block) {
|
||||
if (!block.isCollapsed()) {
|
||||
return 'enabled';
|
||||
}
|
||||
block = block.getNextBlock();
|
||||
}
|
||||
block = block.getNextBlock();
|
||||
}
|
||||
return 'disabled';
|
||||
}
|
||||
return 'disabled';
|
||||
}
|
||||
return 'hidden';
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
Blockly.ContextMenuItems.toggleOption_(true, scope.workspace.getTopBlocks(true));
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
|
||||
id: 'collapseWorkspace',
|
||||
weight: 0,
|
||||
};
|
||||
collapseOption.callback = function(scope) {
|
||||
Blockly.ContextMenuItems.toggleOption_(true, scope.workspace.getTopBlocks(true));
|
||||
};
|
||||
collapseOption.scopeType = Blockly.ContextMenuRegistry.ScopeType.WORKSPACE;
|
||||
collapseOption.id = 'collapseWorkspace';
|
||||
collapseOption.weight = 0;
|
||||
Blockly.ContextMenuRegistry.registry.register(collapseOption);
|
||||
};
|
||||
|
||||
/** Option to expand all blocks. */
|
||||
Blockly.ContextMenuItems.registerExpand = function() {
|
||||
var expandOption = {};
|
||||
expandOption.displayText = function() {
|
||||
return Blockly.Msg['EXPAND_ALL'];
|
||||
};
|
||||
expandOption.preconditionFn = function(scope) {
|
||||
if (scope.workspace.options.collapse) {
|
||||
var topBlocks = scope.workspace.getTopBlocks(false);
|
||||
for (var i = 0; i < topBlocks.length; i++) {
|
||||
var block = topBlocks[i];
|
||||
while (block) {
|
||||
if (block.isCollapsed()) {
|
||||
return 'enabled';
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var expandOption = {
|
||||
displayText: function() {
|
||||
return Blockly.Msg['EXPAND_ALL'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (scope.workspace.options.collapse) {
|
||||
var topBlocks = scope.workspace.getTopBlocks(false);
|
||||
for (var i = 0; i < topBlocks.length; i++) {
|
||||
var block = topBlocks[i];
|
||||
while (block) {
|
||||
if (block.isCollapsed()) {
|
||||
return 'enabled';
|
||||
}
|
||||
block = block.getNextBlock();
|
||||
}
|
||||
block = block.getNextBlock();
|
||||
}
|
||||
return 'disabled';
|
||||
}
|
||||
return 'disabled';
|
||||
}
|
||||
return 'hidden';
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
Blockly.ContextMenuItems.toggleOption_(false, scope.workspace.getTopBlocks(true));
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
|
||||
id: 'toggleWorkspace',
|
||||
weight: 0,
|
||||
};
|
||||
expandOption.callback = function(scope) {
|
||||
Blockly.ContextMenuItems.toggleOption_(false, scope.workspace.getTopBlocks(true));
|
||||
};
|
||||
expandOption.scopeType = Blockly.ContextMenuRegistry.ScopeType.WORKSPACE;
|
||||
expandOption.id = 'toggleWorkspace';
|
||||
expandOption.weight = 0;
|
||||
Blockly.ContextMenuRegistry.registry.register(expandOption);
|
||||
};
|
||||
|
||||
@@ -218,42 +228,51 @@ Blockly.ContextMenuItems.deleteNext_ = function(deleteList, eventGroup) {
|
||||
|
||||
/** Option to delete all blocks. */
|
||||
Blockly.ContextMenuItems.registerDeleteAll = function() {
|
||||
var deleteOption = {};
|
||||
deleteOption.displayText = function(scope) {
|
||||
var deletableBlocksLength =
|
||||
Blockly.ContextMenuItems.getDeletableBlocks_(scope.workspace).length;
|
||||
if (deletableBlocksLength == 1) {
|
||||
return Blockly.Msg['DELETE_BLOCK'];
|
||||
} else {
|
||||
return Blockly.Msg['DELETE_X_BLOCKS'].replace('%1', String(deletableBlocksLength));
|
||||
}
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var deleteOption = {
|
||||
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (!scope.workspace) {
|
||||
return;
|
||||
}
|
||||
var deletableBlocksLength =
|
||||
Blockly.ContextMenuItems.getDeletableBlocks_(scope.workspace).length;
|
||||
if (deletableBlocksLength == 1) {
|
||||
return Blockly.Msg['DELETE_BLOCK'];
|
||||
} else {
|
||||
return Blockly.Msg['DELETE_X_BLOCKS'].replace('%1', String(deletableBlocksLength));
|
||||
}
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (!scope.workspace) {
|
||||
return;
|
||||
}
|
||||
var deletableBlocksLength =
|
||||
Blockly.ContextMenuItems.getDeletableBlocks_(scope.workspace).length;
|
||||
return deletableBlocksLength > 0 ? 'enabled' : 'disabled';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (!scope.workspace) {
|
||||
return;
|
||||
}
|
||||
scope.workspace.cancelCurrentGesture();
|
||||
var deletableBlocks = Blockly.ContextMenuItems.getDeletableBlocks_(scope.workspace);
|
||||
var eventGroup = Blockly.utils.genUid();
|
||||
if (deletableBlocks.length < 2) {
|
||||
Blockly.ContextMenuItems.deleteNext_(deletableBlocks, eventGroup);
|
||||
} else {
|
||||
Blockly.confirm(
|
||||
Blockly.Msg['DELETE_ALL_BLOCKS'].replace('%1', deletableBlocks.length),
|
||||
function(ok) {
|
||||
if (ok) {
|
||||
Blockly.ContextMenuItems.deleteNext_(deletableBlocks, eventGroup);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
|
||||
id: 'workspaceDelete',
|
||||
weight: 0,
|
||||
};
|
||||
deleteOption.preconditionFn = function(scope) {
|
||||
var deletableBlocksLength =
|
||||
Blockly.ContextMenuItems.getDeletableBlocks_(scope.workspace).length;
|
||||
return deletableBlocksLength > 0 ? 'enabled' : 'disabled';
|
||||
};
|
||||
deleteOption.callback = function(scope) {
|
||||
if (scope.workspace.currentGesture_) {
|
||||
scope.workspace.currentGesture_.cancel();
|
||||
}
|
||||
var deletableBlocks = Blockly.ContextMenuItems.getDeletableBlocks_(scope.workspace);
|
||||
var eventGroup = Blockly.utils.genUid();
|
||||
if (deletableBlocks.length < 2) {
|
||||
Blockly.ContextMenuItems.deleteNext_(deletableBlocks, eventGroup);
|
||||
} else {
|
||||
Blockly.confirm(
|
||||
Blockly.Msg['DELETE_ALL_BLOCKS'].replace('%1', deletableBlocks.length),
|
||||
function(ok) {
|
||||
if (ok) {
|
||||
Blockly.ContextMenuItems.deleteNext_(deletableBlocks, eventGroup);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
deleteOption.scopeType = Blockly.ContextMenuRegistry.ScopeType.WORKSPACE;
|
||||
deleteOption.id = 'workspaceDelete';
|
||||
deleteOption.weight = 0;
|
||||
Blockly.ContextMenuRegistry.registry.register(deleteOption);
|
||||
};
|
||||
|
||||
@@ -270,6 +289,239 @@ Blockly.ContextMenuItems.registerWorkspaceOptions_ = function() {
|
||||
Blockly.ContextMenuItems.registerDeleteAll();
|
||||
};
|
||||
|
||||
/** Option to duplicate a block. */
|
||||
Blockly.ContextMenuItems.registerDuplicate = function() {
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var duplicateOption = {
|
||||
displayText: function() {
|
||||
return Blockly.Msg['DUPLICATE_BLOCK'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
var block = scope.block;
|
||||
if (!block.isInFlyout && block.isDeletable() && block.isMovable()) {
|
||||
if (block.isDuplicatable()) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'disabled';
|
||||
}
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (scope.block) {
|
||||
Blockly.duplicate(scope.block);
|
||||
}
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
|
||||
id: 'blockDuplicate',
|
||||
weight: 0,
|
||||
};
|
||||
Blockly.ContextMenuRegistry.registry.register(duplicateOption);
|
||||
};
|
||||
|
||||
/** Option to add or remove block-level comment. */
|
||||
Blockly.ContextMenuItems.registerComment = function() {
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var commentOption = {
|
||||
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (scope.block.getCommentIcon()) {
|
||||
// If there's already a comment, option is to remove.
|
||||
return Blockly.Msg['REMOVE_COMMENT'];
|
||||
}
|
||||
// If there's no comment yet, option is to add.
|
||||
return Blockly.Msg['ADD_COMMENT'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
var block = scope.block;
|
||||
// IE doesn't support necessary features for comment editing.
|
||||
if (!Blockly.utils.userAgent.IE && !block.isInFlyout && block.workspace.options.comments &&
|
||||
!block.isCollapsed() && block.isEditable()) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
var block = scope.block;
|
||||
if (block.getCommentIcon()) {
|
||||
block.setCommentText(null);
|
||||
} else {
|
||||
block.setCommentText('');
|
||||
}
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
|
||||
id: 'blockComment',
|
||||
weight: 0,
|
||||
};
|
||||
Blockly.ContextMenuRegistry.registry.register(commentOption);
|
||||
};
|
||||
|
||||
/** Option to inline variables. */
|
||||
Blockly.ContextMenuItems.registerInline = function() {
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var inlineOption = {
|
||||
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
return (scope.block.getInputsInline()) ?
|
||||
Blockly.Msg['EXTERNAL_INPUTS'] : Blockly.Msg['INLINE_INPUTS'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
var block = scope.block;
|
||||
if (!block.isInFlyout && block.isMovable() && !block.isCollapsed()) {
|
||||
for (var i = 1; i < block.inputList.length; i++) {
|
||||
// Only display this option if there are two value or dummy inputs next to each other.
|
||||
if (block.inputList[i - 1].type != Blockly.NEXT_STATEMENT &&
|
||||
block.inputList[i].type != Blockly.NEXT_STATEMENT) {
|
||||
return 'enabled';
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
scope.block.setInputsInline(!scope.block.getInputsInline());
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
|
||||
id: 'blockInline',
|
||||
weight: 0,
|
||||
};
|
||||
Blockly.ContextMenuRegistry.registry.register(inlineOption);
|
||||
};
|
||||
|
||||
/** Option to collapse or expand a block. */
|
||||
Blockly.ContextMenuItems.registerCollapseExpandBlock = function() {
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var collapseExpandOption = {
|
||||
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (scope.block.isCollapsed()) {
|
||||
return Blockly.Msg['EXPAND_BLOCK'];
|
||||
}
|
||||
return Blockly.Msg['COLLAPSE_BLOCK'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
var block = scope.block;
|
||||
if (!block.isInFlyout && block.isMovable()) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
scope.block.setCollapsed(!scope.block.isCollapsed());
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
|
||||
id: 'blockCollapseExpand',
|
||||
weight: 0,
|
||||
};
|
||||
Blockly.ContextMenuRegistry.registry.register(collapseExpandOption);
|
||||
};
|
||||
|
||||
/** Option to disable or enable a block. */
|
||||
Blockly.ContextMenuItems.registerDisable = function() {
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var disableOption = {
|
||||
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
return (scope.block.isEnabled()) ?
|
||||
Blockly.Msg['DISABLE_BLOCK'] : Blockly.Msg['ENABLE_BLOCK'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
var block = scope.block;
|
||||
if (!block.isInFlyout && block.workspace.options.disable && block.isEditable()) {
|
||||
if (block.getInheritedDisabled()) {
|
||||
return 'disabled';
|
||||
}
|
||||
return 'enabled';
|
||||
}
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
var block = scope.block;
|
||||
var group = Blockly.Events.getGroup();
|
||||
if (!group) {
|
||||
Blockly.Events.setGroup(true);
|
||||
}
|
||||
block.setEnabled(!block.isEnabled());
|
||||
if (!group) {
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
|
||||
id: 'blockDisable',
|
||||
weight: 0,
|
||||
};
|
||||
Blockly.ContextMenuRegistry.registry.register(disableOption);
|
||||
};
|
||||
|
||||
/** Option to delete a block. */
|
||||
Blockly.ContextMenuItems.registerDelete = function() {
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var deleteOption = {
|
||||
displayText: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
var block = scope.block;
|
||||
// Count the number of blocks that are nested in this block.
|
||||
var descendantCount = block.getDescendants(false).length;
|
||||
var nextBlock = block.getNextBlock();
|
||||
if (nextBlock) {
|
||||
// Blocks in the current stack would survive this block's deletion.
|
||||
descendantCount -= nextBlock.getDescendants(false).length;
|
||||
}
|
||||
return (descendantCount == 1) ? Blockly.Msg['DELETE_BLOCK'] :
|
||||
Blockly.Msg['DELETE_X_BLOCKS'].replace('%1', String(descendantCount));
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
if (!scope.block.isInFlyout && scope.block.isDeletable()) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
Blockly.Events.setGroup(true);
|
||||
scope.block.dispose(true, true);
|
||||
Blockly.Events.setGroup(false);
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
|
||||
id: 'blockDelete',
|
||||
weight: 0,
|
||||
};
|
||||
Blockly.ContextMenuRegistry.registry.register(deleteOption);
|
||||
};
|
||||
|
||||
/** Option to open help for a block. */
|
||||
Blockly.ContextMenuItems.registerHelp = function() {
|
||||
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
|
||||
var helpOption = {
|
||||
displayText: function() {
|
||||
return Blockly.Msg['HELP'];
|
||||
},
|
||||
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
var block = scope.block;
|
||||
var url = (typeof block.helpUrl == 'function') ?
|
||||
block.helpUrl() : block.helpUrl;
|
||||
if (url) {
|
||||
return 'enabled';
|
||||
}
|
||||
return 'hidden';
|
||||
},
|
||||
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
|
||||
scope.block.showHelp();
|
||||
},
|
||||
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
|
||||
id: 'blockHelp',
|
||||
weight: 0,
|
||||
};
|
||||
Blockly.ContextMenuRegistry.registry.register(helpOption);
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers all block-scoped context menu items.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ContextMenuItems.registerBlockOptions_ = function() {
|
||||
Blockly.ContextMenuItems.registerDuplicate();
|
||||
Blockly.ContextMenuItems.registerComment();
|
||||
Blockly.ContextMenuItems.registerInline();
|
||||
Blockly.ContextMenuItems.registerCollapseExpandBlock();
|
||||
Blockly.ContextMenuItems.registerDisable();
|
||||
Blockly.ContextMenuItems.registerDelete();
|
||||
Blockly.ContextMenuItems.registerHelp();
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers all default context menu items. This should be called once per instance of
|
||||
* ContextMenuRegistry.
|
||||
@@ -277,5 +529,6 @@ Blockly.ContextMenuItems.registerWorkspaceOptions_ = function() {
|
||||
*/
|
||||
Blockly.ContextMenuItems.registerDefaultOptions = function() {
|
||||
Blockly.ContextMenuItems.registerWorkspaceOptions_();
|
||||
Blockly.ContextMenuItems.registerBlockOptions_();
|
||||
};
|
||||
|
||||
|
||||
@@ -558,6 +558,24 @@ Blockly.Workspace.prototype.hasBlockLimits = function() {
|
||||
return this.options.maxBlocks != Infinity || !!this.options.maxInstances;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the undo stack for workplace.
|
||||
* @return {!Array.<!Blockly.Events.Abstract>} undo stack
|
||||
* @package
|
||||
*/
|
||||
Blockly.Workspace.prototype.getUndoStack = function() {
|
||||
return this.undoStack_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the redo stack for workplace.
|
||||
* @return {!Array.<!Blockly.Events.Abstract>} redo stack
|
||||
* @package
|
||||
*/
|
||||
Blockly.Workspace.prototype.getRedoStack = function() {
|
||||
return this.redoStack_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo or redo the previous action.
|
||||
* @param {boolean} redo False if undo, true if redo.
|
||||
|
||||
Reference in New Issue
Block a user