Files
blockly/core/shortcut_items.js

278 lines
8.5 KiB
JavaScript

/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Registers default keyboard shortcuts.
*/
'use strict';
/**
* Registers default keyboard shortcuts.
* @namespace Blockly.ShortcutItems
*/
goog.module('Blockly.ShortcutItems');
const clipboard = goog.require('Blockly.clipboard');
const common = goog.require('Blockly.common');
const {BlockSvg} = goog.require('Blockly.BlockSvg');
const {Gesture} = goog.require('Blockly.Gesture');
/* eslint-disable-next-line no-unused-vars */
const {ICopyable} = goog.requireType('Blockly.ICopyable');
const {KeyCodes} = goog.require('Blockly.utils.KeyCodes');
const {ShortcutRegistry} = goog.require('Blockly.ShortcutRegistry');
/**
* Object holding the names of the default shortcut items.
* @enum {string}
* @alias Blockly.ShortcutItems.names
*/
const names = {
ESCAPE: 'escape',
DELETE: 'delete',
COPY: 'copy',
CUT: 'cut',
PASTE: 'paste',
UNDO: 'undo',
REDO: 'redo',
};
exports.names = names;
/**
* Keyboard shortcut to hide chaff on escape.
* @alias Blockly.ShortcutItems.registerEscape
*/
const registerEscape = function() {
/** @type {!ShortcutRegistry.KeyboardShortcut} */
const escapeAction = {
name: names.ESCAPE,
preconditionFn: function(workspace) {
return !workspace.options.readOnly;
},
callback: function(workspace) {
workspace.hideChaff();
return true;
},
keyCodes: [KeyCodes.ESC],
};
ShortcutRegistry.registry.register(escapeAction);
};
exports.registerEscape = registerEscape;
/**
* Keyboard shortcut to delete a block on delete or backspace
* @alias Blockly.ShortcutItems.registerDelete
*/
const registerDelete = function() {
/** @type {!ShortcutRegistry.KeyboardShortcut} */
const deleteShortcut = {
name: names.DELETE,
preconditionFn: function(workspace) {
const selected = common.getSelected();
return !workspace.options.readOnly && selected && selected.isDeletable();
},
callback: function(workspace, e) {
// Delete or backspace.
// Stop the browser from going back to the previous page.
// Do this first to prevent an error in the delete code from resulting in
// data loss.
e.preventDefault();
// Don't delete while dragging. Jeez.
if (Gesture.inProgress()) {
return false;
}
(/** @type {!BlockSvg} */ (common.getSelected())).checkAndDelete();
return true;
},
keyCodes: [KeyCodes.DELETE, KeyCodes.BACKSPACE],
};
ShortcutRegistry.registry.register(deleteShortcut);
};
exports.registerDelete = registerDelete;
/**
* Keyboard shortcut to copy a block on ctrl+c, cmd+c, or alt+c.
* @alias Blockly.ShortcutItems.registerCopy
*/
const registerCopy = function() {
const ctrlC = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.C, [KeyCodes.CTRL]);
const altC =
ShortcutRegistry.registry.createSerializedKey(KeyCodes.C, [KeyCodes.ALT]);
const metaC = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.C, [KeyCodes.META]);
/** @type {!ShortcutRegistry.KeyboardShortcut} */
const copyShortcut = {
name: names.COPY,
preconditionFn: function(workspace) {
const selected = common.getSelected();
return !workspace.options.readOnly && !Gesture.inProgress() && selected &&
selected.isDeletable() && selected.isMovable();
},
callback: function(workspace, e) {
// Prevent the default copy behavior, which may beep or otherwise indicate
// an error due to the lack of a selection.
e.preventDefault();
workspace.hideChaff();
clipboard.copy(/** @type {!ICopyable} */ (common.getSelected()));
return true;
},
keyCodes: [ctrlC, altC, metaC],
};
ShortcutRegistry.registry.register(copyShortcut);
};
exports.registerCopy = registerCopy;
/**
* Keyboard shortcut to copy and delete a block on ctrl+x, cmd+x, or alt+x.
* @alias Blockly.ShortcutItems.registerCut
*/
const registerCut = function() {
const ctrlX = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.X, [KeyCodes.CTRL]);
const altX =
ShortcutRegistry.registry.createSerializedKey(KeyCodes.X, [KeyCodes.ALT]);
const metaX = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.X, [KeyCodes.META]);
/** @type {!ShortcutRegistry.KeyboardShortcut} */
const cutShortcut = {
name: names.CUT,
preconditionFn: function(workspace) {
const selected = common.getSelected();
return !workspace.options.readOnly && !Gesture.inProgress() && selected &&
selected instanceof BlockSvg && selected.isDeletable() &&
selected.isMovable() && !selected.workspace.isFlyout;
},
callback: function() {
const selected = common.getSelected();
if (!selected) {
// Shouldn't happen but appeases the type system
return false;
}
clipboard.copy(selected);
(/** @type {!BlockSvg} */ (selected)).checkAndDelete();
return true;
},
keyCodes: [ctrlX, altX, metaX],
};
ShortcutRegistry.registry.register(cutShortcut);
};
exports.registerCut = registerCut;
/**
* Keyboard shortcut to paste a block on ctrl+v, cmd+v, or alt+v.
* @alias Blockly.ShortcutItems.registerPaste
*/
const registerPaste = function() {
const ctrlV = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.V, [KeyCodes.CTRL]);
const altV =
ShortcutRegistry.registry.createSerializedKey(KeyCodes.V, [KeyCodes.ALT]);
const metaV = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.V, [KeyCodes.META]);
/** @type {!ShortcutRegistry.KeyboardShortcut} */
const pasteShortcut = {
name: names.PASTE,
preconditionFn: function(workspace) {
return !workspace.options.readOnly && !Gesture.inProgress();
},
callback: function() {
return clipboard.paste();
},
keyCodes: [ctrlV, altV, metaV],
};
ShortcutRegistry.registry.register(pasteShortcut);
};
exports.registerPaste = registerPaste;
/**
* Keyboard shortcut to undo the previous action on ctrl+z, cmd+z, or alt+z.
* @alias Blockly.ShortcutItems.registerUndo
*/
const registerUndo = function() {
const ctrlZ = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.Z, [KeyCodes.CTRL]);
const altZ =
ShortcutRegistry.registry.createSerializedKey(KeyCodes.Z, [KeyCodes.ALT]);
const metaZ = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.Z, [KeyCodes.META]);
/** @type {!ShortcutRegistry.KeyboardShortcut} */
const undoShortcut = {
name: names.UNDO,
preconditionFn: function(workspace) {
return !workspace.options.readOnly && !Gesture.inProgress();
},
callback: function(workspace) {
// 'z' for undo 'Z' is for redo.
workspace.hideChaff();
workspace.undo(false);
return true;
},
keyCodes: [ctrlZ, altZ, metaZ],
};
ShortcutRegistry.registry.register(undoShortcut);
};
exports.registerUndo = registerUndo;
/**
* Keyboard shortcut to redo the previous action on ctrl+shift+z, cmd+shift+z,
* or alt+shift+z.
* @alias Blockly.ShortcutItems.registerRedo
*/
const registerRedo = function() {
const ctrlShiftZ = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.Z, [KeyCodes.SHIFT, KeyCodes.CTRL]);
const altShiftZ = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.Z, [KeyCodes.SHIFT, KeyCodes.ALT]);
const metaShiftZ = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.Z, [KeyCodes.SHIFT, KeyCodes.META]);
// Ctrl-y is redo in Windows. Command-y is never valid on Macs.
const ctrlY = ShortcutRegistry.registry.createSerializedKey(
KeyCodes.Y, [KeyCodes.CTRL]);
/** @type {!ShortcutRegistry.KeyboardShortcut} */
const redoShortcut = {
name: names.REDO,
preconditionFn: function(workspace) {
return !Gesture.inProgress() && !workspace.options.readOnly;
},
callback: function(workspace) {
// 'z' for undo 'Z' is for redo.
workspace.hideChaff();
workspace.undo(true);
return true;
},
keyCodes: [ctrlShiftZ, altShiftZ, metaShiftZ, ctrlY],
};
ShortcutRegistry.registry.register(redoShortcut);
};
exports.registerRedo = registerRedo;
/**
* Registers all default keyboard shortcut item. This should be called once per
* instance of KeyboardShortcutRegistry.
* @alias Blockly.ShortcutItems.registerDefaultShortcuts
* @package
*/
const registerDefaultShortcuts = function() {
registerEscape();
registerDelete();
registerCopy();
registerCut();
registerPaste();
registerUndo();
registerRedo();
};
exports.registerDefaultShortcuts = registerDefaultShortcuts;
registerDefaultShortcuts();