Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/blockly/core/shortcut_items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export enum names {
PREVIOUS_STACK = 'previous_stack',
INFORMATION = 'information',
PERFORM_ACTION = 'perform_action',
DUPLICATE = 'duplicate',
}

/**
Expand Down Expand Up @@ -870,6 +871,34 @@ export function registerPerformAction() {
ShortcutRegistry.registry.register(performActionShortcut);
}

/**
* Registers keyboard shortcut to duplicate a block or workspace comment.
*/
export function registerDuplicate() {
const duplicateShortcut: KeyboardShortcut = {
name: names.DUPLICATE,
preconditionFn: (workspace, scope) => {
const {focusedNode} = scope;
return (
!workspace.isDragging() &&
!workspace.isReadOnly() &&
(focusedNode instanceof BlockSvg ? focusedNode.isDuplicatable() : true)
);
},
callback: (workspace, _e, _shortcut, scope) => {
keyboardNavigationController.setIsActive(true);
const copyable = isICopyable(scope.focusedNode) && scope.focusedNode;
if (!copyable) return false;
const data = copyable.toCopyData();
if (!data) return false;
return !!clipboard.paste(data, workspace);
},
keyCodes: [KeyCodes.D],
allowCollision: true,
};
ShortcutRegistry.registry.register(duplicateShortcut);
}

/**
* Registers all default keyboard shortcut item. This should be called once per
* instance of KeyboardShortcutRegistry.
Expand Down Expand Up @@ -899,6 +928,7 @@ export function registerKeyboardNavigationShortcuts() {
registerDisconnectBlock();
registerStackNavigation();
registerPerformAction();
registerDuplicate();
}

/**
Expand Down
58 changes: 58 additions & 0 deletions packages/blockly/tests/mocha/shortcut_items_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1231,4 +1231,62 @@ suite('Keyboard Shortcut Items', function () {
this.workspace.registerButtonCallback('CREATE_VARIABLE', oldCallback);
});
});

suite('Duplicate (D)', function () {
test('Can duplicate blocks', function () {
const block = this.workspace.newBlock('controls_if');
Blockly.getFocusManager().focusNode(block);
assert.equal(this.workspace.getTopBlocks().length, 1);
const event = createKeyDownEvent(Blockly.utils.KeyCodes.D);
this.workspace.getInjectionDiv().dispatchEvent(event);
const topBlocks = this.workspace.getTopBlocks(true);
assert.equal(topBlocks.length, 2);
assert.notEqual(topBlocks[1], block);
assert.equal(topBlocks[1].type, block.type);
});

test('Can duplicate workspace comments', function () {
const comment = this.workspace.newComment();
comment.setText('Hello');
Blockly.getFocusManager().focusNode(comment);
assert.equal(this.workspace.getTopComments().length, 1);
const event = createKeyDownEvent(Blockly.utils.KeyCodes.D);
this.workspace.getInjectionDiv().dispatchEvent(event);
const topComments = this.workspace.getTopComments(true);
assert.equal(topComments.length, 2);
assert.notEqual(topComments[1], comment);
assert.equal(topComments[1].getText(), comment.getText());
});

test('Does not duplicate blocks on a readonly workspace', function () {
const block = this.workspace.newBlock('controls_if');
this.workspace.setIsReadOnly(true);
Blockly.getFocusManager().focusNode(block);
assert.equal(this.workspace.getTopBlocks().length, 1);
const event = createKeyDownEvent(Blockly.utils.KeyCodes.D);
this.workspace.getInjectionDiv().dispatchEvent(event);
assert.equal(this.workspace.getTopBlocks().length, 1);
});

test('Does not duplicate blocks that are not duplicatable', function () {
const block = this.workspace.newBlock('controls_if');
this.workspace.options.maxBlocks = 1;
assert.isFalse(block.isDuplicatable());
assert.equal(this.workspace.getTopBlocks().length, 1);
const event = createKeyDownEvent(Blockly.utils.KeyCodes.D);
this.workspace.getInjectionDiv().dispatchEvent(event);
assert.equal(this.workspace.getTopBlocks().length, 1);
});

test('Does not duplicate workspace comments on a readonly workspace', function () {
const comment = this.workspace.newComment();
comment.setText('Hello');
this.workspace.setIsReadOnly(true);
Blockly.getFocusManager().focusNode(comment);
assert.equal(this.workspace.getTopComments().length, 1);
const event = createKeyDownEvent(Blockly.utils.KeyCodes.D);
this.workspace.getInjectionDiv().dispatchEvent(event);
assert.equal(this.workspace.getTopComments().length, 1);
});
});
});