Closed maribethb closed 2 years ago
I tried to implement this and ran into problems with browser clipboard APIs not being fully usable/implemented yet. However, there's a fallback option that would be a good first project for a contributor.
Use a hidden textarea and populate it with the expected copy text, then have the browser execute a copy from there using document.executeCommand. This page has a walkthrough of the code needed.
Follow the context menu codelab to add the option.
Hello! I think this plugin is very useful and a feature that many people want. I wonder why it has never existed before. I am very interested in this feature request.
Is the code for this plugin published somewhere? Or is it still under development or proposal? Beta version is fine, so I'd like to see the code if it's available. Can you do it?
This is still in development and we hope to have something this quarter. We will post here when it is ready for use.
Hi, Not sure if this is what you are looking for, but I have implemented something like this by adding an extra option to the context menu and storing the blocks in the browser's local storage. If on the other tab the key is present in the local storage, an option to paste is available in the context menu.
Blockly.ContextMenuItems.blockCopyToStorage = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var copyToStorageOption = {
displayText: function() {
return 'Copy to stash';
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
return 'enabled';
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
blockDom = Blockly.Xml.blockToDomWithXY(scope.block);
blockText = Blockly.Xml.domToText(blockDom);
localStorage.setItem('blocklyStash', blockText);
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockCopyToStorage',
weight: 0,
};
Blockly.ContextMenuRegistry.registry.register(copyToStorageOption);
};
Blockly.ContextMenuItems.blockPasteFromStorage = function() {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var pasteFromStorageOption = {
displayText: function() {
return 'Paste from stash';
},
preconditionFn: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
if (localStorage.getItem('blocklyStash')){
return 'enabled';
} else {
return 'disabled';
}
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
blockText = localStorage.getItem('blocklyStash');
blockDom = Blockly.Xml.textToDom(blockText);
Blockly.Xml.domToBlock(blockDom, workspace);
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
id: 'blockPasteFromStorage',
weight: 0,
};
Blockly.ContextMenuRegistry.registry.register(pasteFromStorageOption);
};
// Register the menus
Blockly.ContextMenuItems.blockCopyToStorage();
Blockly.ContextMenuItems.blockPasteFromStorage();
I've looked carefully and it looks like there's no progress on this issue, but is there anything different? I strongly want this. If there is no progress for a long time, I am thinking of implementing it with this code. https://github.com/google/blockly-samples/issues/371#issuecomment-762264332
I wrote the code based on # 371. I thought about sending a pull request, but I didn't have a deep understanding of npm and couldn't do the same, so I stopped. The name is tentatively copyByStorage. I hope you find this code useful.
how to use
Load this code after loading blockly in your browser.
Blockly.copyByStorage.init(contextMenu, shortcut, unregisterDuplicate);
Argument description
unregisterDuplicate {boolean} Unregister the context menu duplication command
All arguments are true by default. code
Blockly.copyByStorage = {};
Blockly.ContextMenuItems.blockCopyToStorage = function () {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var copyToStorageOption = {
displayText: function () {
return Blockly.Msg['COPYBYSTORAGE_COPY'];
},
preconditionFn: function (/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
return 'enabled';
},
callback: function (/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
let blockDom = Blockly.Xml.blockToDomWithXY(scope.block);
Blockly.Xml.deleteNext(blockDom);
let blockText = Blockly.Xml.domToText(blockDom);
Blockly.copyByStorage.__Storage.setItem('blocklyStash', blockText);
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
id: 'blockCopyToStorage',
weight: 0,
};
Blockly.ContextMenuRegistry.registry.register(copyToStorageOption);
};
Blockly.ContextMenuItems.blockPasteFromStorage = function () {
/** @type {!Blockly.ContextMenuRegistry.RegistryItem} */
var pasteFromStorageOption = {
displayText: function () {
return Blockly.Msg['COPYBYSTORAGE_PASTE'];
},
preconditionFn: function (/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
if (Blockly.copyByStorage.__Storage.getItem('blocklyStash')) {
return 'enabled';
}
return 'disabled';
},
callback: function (/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
let blockText = Blockly.copyByStorage.__Storage.getItem('blocklyStash');
let blockDom = Blockly.Xml.textToDom(blockText);
Blockly.Xml.domToBlock(blockDom, workspace);
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
id: 'blockPasteFromStorage',
weight: 0,
};
Blockly.ContextMenuRegistry.registry.register(pasteFromStorageOption);
};
Blockly.ShortcutItems.blockCopyToStorage = function () { /* @type {!ShortcutRegistry.KeyboardShortcut} / const copyShortcut = { name: 'copy', preconditionFn: function (workspace) { return !workspace.options.readOnly && !Blockly.Gesture.inProgress() && Blockly.selected && Blockly.selected.isDeletable() && Blockly.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(); let blockDom = Blockly.Xml.blockToDomWithXY(Blockly.selected); Blockly.Xml.deleteNext(blockDom); let blockText = Blockly.Xml.domToText(blockDom); Blockly.copyByStorage.__Storage.setItem('blocklyStash', blockText); return true; }, }; Blockly.ShortcutRegistry.registry.register(copyShortcut);
const ctrlC = Blockly.ShortcutRegistry.registry.createSerializedKey(
Blockly.utils.KeyCodes.C, [Blockly.utils.KeyCodes.CTRL]);
Blockly.ShortcutRegistry.registry.addKeyMapping(ctrlC, copyShortcut.name);
const altC =
Blockly.ShortcutRegistry.registry.createSerializedKey(
Blockly.utils.KeyCodes.C, [Blockly.utils.KeyCodes.ALT]);
Blockly.ShortcutRegistry.registry.addKeyMapping(altC, copyShortcut.name);
const metaC = Blockly.ShortcutRegistry.registry.createSerializedKey(
Blockly.utils.KeyCodes.C, [Blockly.utils.KeyCodes.META]);
Blockly.ShortcutRegistry.registry.addKeyMapping(metaC, copyShortcut.name);
}; Blockly.ShortcutItems.blockCutToStorage = function () { /* @type {!ShortcutRegistry.KeyboardShortcut} / const cutShortcut = { name: 'cut', preconditionFn: function (workspace) { return !workspace.options.readOnly && !Blockly.Gesture.inProgress() && Blockly.selected && Blockly.selected.isDeletable() && Blockly.selected.isMovable() && !Blockly.selected.workspace.isFlyout; }, 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(); let blockDom = Blockly.Xml.blockToDomWithXY(Blockly.selected); Blockly.Xml.deleteNext(blockDom); let blockText = Blockly.Xml.domToText(blockDom); Blockly.copyByStorage.__Storage.setItem('blocklyStash', blockText); Blockly.deleteBlock(Blockly.selected); return true; }, }; Blockly.ShortcutRegistry.registry.register(cutShortcut);
var ctrlX = Blockly.ShortcutRegistry.registry.createSerializedKey(
Blockly.utils.KeyCodes.X, [Blockly.utils.KeyCodes.CTRL]);
Blockly.ShortcutRegistry.registry.addKeyMapping(ctrlX, cutShortcut.name);
var altX = Blockly.ShortcutRegistry.registry.createSerializedKey(
Blockly.utils.KeyCodes.X, [Blockly.utils.KeyCodes.ALT]);
Blockly.ShortcutRegistry.registry.addKeyMapping(altX, cutShortcut.name);
var metaX = Blockly.ShortcutRegistry.registry.createSerializedKey(
Blockly.utils.KeyCodes.X, [Blockly.utils.KeyCodes.META]);
Blockly.ShortcutRegistry.registry.addKeyMapping(metaX, cutShortcut.name);
}; Blockly.ShortcutItems.blockPasteFromStorage = function () { /* @type {!ShortcutRegistry.KeyboardShortcut} / const pasteShortcut = { name: 'paste', preconditionFn: function (workspace) { if (!Blockly.copyByStorage.Storage.getItem('blocklyStash')) { return 'disabled'; } return !workspace.options.readOnly && !Blockly.Gesture.inProgress(); }, 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(); let blockText = Blockly.copyByStorage.Storage.getItem('blocklyStash'); let blockDom = Blockly.Xml.textToDom(blockText); Blockly.Xml.domToBlock(blockDom, workspace); return true; }, }; Blockly.ShortcutRegistry.registry.register(pasteShortcut);
const ctrlV = Blockly.ShortcutRegistry.registry.createSerializedKey(
Blockly.utils.KeyCodes.V, [Blockly.utils.KeyCodes.CTRL]);
Blockly.ShortcutRegistry.registry.addKeyMapping(ctrlV, pasteShortcut.name);
const altV =
Blockly.ShortcutRegistry.registry.createSerializedKey(Blockly.utils.KeyCodes.V, [Blockly.utils.KeyCodes.ALT]);
Blockly.ShortcutRegistry.registry.addKeyMapping(altV, pasteShortcut.name);
const metaV = Blockly.ShortcutRegistry.registry.createSerializedKey(
Blockly.utils.KeyCodes.V, [Blockly.utils.KeyCodes.META]);
Blockly.ShortcutRegistry.registry.addKeyMapping(metaV, pasteShortcut.name);
};
/**
@param {boolean} unregisterDuplicate Unregister the context menu duplication command */ Blockly.copyByStorage.init = function (contextMenu = true, shortcut = true, unregisterDuplicate = true) { // Use localStorage Blockly.copyByStorage.__Storage = localStorage;
if (contextMenu) { // Register the menus Blockly.ContextMenuItems.blockCopyToStorage(); Blockly.ContextMenuItems.blockPasteFromStorage(); }
if (shortcut) { // Unregister the default KeyboardShortcuts Blockly.ShortcutRegistry.registry.unregister(Blockly.ShortcutItems.names.COPY); Blockly.ShortcutRegistry.registry.unregister(Blockly.ShortcutItems.names.CUT); Blockly.ShortcutRegistry.registry.unregister(Blockly.ShortcutItems.names.PASTE); // Register the KeyboardShortcuts Blockly.ShortcutItems.blockCopyToStorage(); Blockly.ShortcutItems.blockCutToStorage(); Blockly.ShortcutItems.blockPasteFromStorage(); }
if (unregisterDuplicate) { // Unregister the context menu duplication command Blockly.ContextMenuRegistry.registry.unregister('blockDuplicate'); } };
Isn't this closed because there is this? https://github.com/google/blockly-samples/tree/master/plugins/cross-tab-copy-paste
Yup! Closed via #1037
Category
Plugin
Details
Create a plugin that allows cross-tab copy and paste (copy block xml to the browser paste buffer, and on paste read the xml and create the block). This is a good candidate for a plugin because the copy/paste APIs are not fully compatible with IE so it may not work well in core blockly.