Merge pull request #5237 from rachel-fenichel/clipboard

Move clipboard functions to a separate namespace
This commit is contained in:
Rachel Fenichel
2021-07-30 11:10:36 -07:00
committed by GitHub
7 changed files with 104 additions and 82 deletions

View File

@@ -17,6 +17,7 @@
goog.provide('Blockly');
goog.require('Blockly.browserEvents');
goog.require('Blockly.clipboard');
goog.require('Blockly.ComponentManager');
goog.require('Blockly.connectionTypes');
goog.require('Blockly.constants');
@@ -91,27 +92,6 @@ Blockly.selected = null;
*/
Blockly.draggingConnections = [];
/**
* Contents of the local clipboard.
* @type {Element}
* @private
*/
Blockly.clipboardXml_ = null;
/**
* Source of the local clipboard.
* @type {Blockly.WorkspaceSvg}
* @private
*/
Blockly.clipboardSource_ = null;
/**
* Map of types to type counts for the clipboard object and descendants.
* @type {Object}
* @private
*/
Blockly.clipboardTypeCounts_ = null;
/**
* Cached value for whether 3D is supported.
* @type {?boolean}
@@ -136,9 +116,7 @@ Blockly.svgSize = function(svg) {
// When removing this function, remove svg.cachedWidth_ and svg.cachedHeight_
// from setCachedParentSvgSize.
Blockly.utils.deprecation.warn(
'Blockly.svgSize',
'March 2021',
'March 2022',
'Blockly.svgSize', 'March 2021', 'March 2022',
'workspace.getCachedParentSvgSize');
svg = /** @type {?} */ (svg);
return new Blockly.utils.Size(svg.cachedWidth_, svg.cachedHeight_);
@@ -220,7 +198,8 @@ Blockly.deleteBlock = function(selected) {
Blockly.Events.setGroup(true);
Blockly.hideChaff();
if (selected.outputConnection) {
// Do not attempt to heal rows (https://github.com/google/blockly/issues/4832)
// Do not attempt to heal rows
// (https://github.com/google/blockly/issues/4832)
selected.dispose(false, true);
} else {
selected.dispose(/* heal */ true, true);
@@ -234,39 +213,14 @@ Blockly.deleteBlock = function(selected) {
* @param {!Blockly.ICopyable} toCopy Block or Workspace Comment to be copied.
* @package
*/
Blockly.copy = function(toCopy) {
var data = toCopy.toCopyData();
if (data) {
Blockly.clipboardXml_ = data.xml;
Blockly.clipboardSource_ = data.source;
Blockly.clipboardTypeCounts_ = data.typeCounts;
}
};
Blockly.copy = Blockly.clipboard.copy;
/**
* Paste a block or workspace comment on to the main workspace.
* @return {boolean} True if the paste was successful, false otherwise.
* @package
*/
Blockly.paste = function() {
if (!Blockly.clipboardXml_) {
return false;
}
// Pasting always pastes to the main workspace, even if the copy
// started in a flyout workspace.
var workspace = Blockly.clipboardSource_;
if (workspace.isFlyout) {
workspace = workspace.targetWorkspace;
}
if (Blockly.clipboardTypeCounts_ &&
workspace.isCapacityAvailable(Blockly.clipboardTypeCounts_)) {
Blockly.Events.setGroup(true);
workspace.paste(Blockly.clipboardXml_);
Blockly.Events.setGroup(false);
return true;
}
return false;
};
Blockly.paste = Blockly.clipboard.paste;
/**
* Duplicate this block and its children, or a workspace comment.
@@ -274,19 +228,7 @@ Blockly.paste = function() {
* copied.
* @package
*/
Blockly.duplicate = function(toDuplicate) {
// Save the clipboard.
var clipboardXml = Blockly.clipboardXml_;
var clipboardSource = Blockly.clipboardSource_;
// Create a duplicate via a copy/paste operation.
Blockly.copy(toDuplicate);
toDuplicate.workspace.paste(Blockly.clipboardXml_);
// Restore the clipboard.
Blockly.clipboardXml_ = clipboardXml;
Blockly.clipboardSource_ = clipboardSource;
};
Blockly.duplicate = Blockly.clipboard.duplicate;
/**
* Cancel the native context menu, unless the focus is on an HTML input widget.
@@ -402,9 +344,7 @@ Blockly.defineBlocksWithJsonArray = function(jsonArray) {
'Block definition #' + i + ' in JSON array' +
' overwrites prior definition of "' + typename + '".');
}
Blockly.Blocks[typename] = {
init: Blockly.jsonInitFactory_(elem)
};
Blockly.Blocks[typename] = {init: Blockly.jsonInitFactory_(elem)};
}
}
}

80
core/clipboard.js Normal file
View File

@@ -0,0 +1,80 @@
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Blockly's internal clipboard for managing copy-paste.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.module('Blockly.clipboard');
goog.module.declareLegacyNamespace();
const Events = goog.require('Blockly.Events');
/* eslint-disable-next-line no-unused-vars */
const ICopyable = goog.requireType('Blockly.ICopyable');
/**
* Metadata about the object that is currently on the clipboard.
* @type {?ICopyable.CopyData}
*/
let copyData = null;
/**
* Copy a block or workspace comment onto the local clipboard.
* @param {!ICopyable} toCopy Block or Workspace Comment to be copied.
*/
const copy = function(toCopy) {
copyData = toCopy.toCopyData();
};
/** @package */
exports.copy = copy;
/**
* Paste a block or workspace comment on to the main workspace.
* @return {boolean} True if the paste was successful, false otherwise.
*/
const paste = function() {
if (!copyData.xml) {
return false;
}
// Pasting always pastes to the main workspace, even if the copy
// started in a flyout workspace.
var workspace = copyData.source;
if (workspace.isFlyout) {
workspace = workspace.targetWorkspace;
}
if (copyData.typeCounts &&
workspace.isCapacityAvailable(copyData.typeCounts)) {
Events.setGroup(true);
workspace.paste(copyData.xml);
Events.setGroup(false);
return true;
}
return false;
};
/** @package */
exports.paste = paste;
/**
* Duplicate this block and its children, or a workspace comment.
* @param {!ICopyable} toDuplicate Block or Workspace Comment to be
* duplicated.
*/
const duplicate = function(toDuplicate) {
// Save the clipboard.
const oldCopyData = copyData;
// Create a duplicate via a copy/paste operation.
copy(toDuplicate);
// copy() replaced the value of copyData.
toDuplicate.workspace.paste(copyData.xml);
// Restore the clipboard.
copyData = oldCopyData;
};
/** @package */
exports.duplicate = duplicate;

View File

@@ -15,7 +15,7 @@
* @namespace
*/
goog.provide('Blockly.ContextMenuItems');
goog.require('Blockly.clipboard');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
goog.require('Blockly.ContextMenuRegistry');
@@ -316,7 +316,7 @@ Blockly.ContextMenuItems.registerDuplicate = function() {
},
callback: function(/** @type {!Blockly.ContextMenuRegistry.Scope} */ scope) {
if (scope.block) {
Blockly.duplicate(scope.block);
Blockly.clipboard.duplicate(scope.block);
}
},
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,

View File

@@ -16,6 +16,7 @@
*/
goog.provide('Blockly.ShortcutItems');
goog.require('Blockly.clipboard');
goog.require('Blockly.Gesture');
goog.require('Blockly.ShortcutRegistry');
goog.require('Blockly.utils.KeyCodes');
@@ -104,7 +105,7 @@ Blockly.ShortcutItems.registerCopy = function() {
// an error due to the lack of a selection.
e.preventDefault();
Blockly.hideChaff();
Blockly.copy(/** @type {!Blockly.ICopyable} */ (Blockly.selected));
Blockly.clipboard.copy(/** @type {!Blockly.ICopyable} */ (Blockly.selected));
return true;
}
};
@@ -137,7 +138,7 @@ Blockly.ShortcutItems.registerCut = function() {
!Blockly.selected.workspace.isFlyout;
},
callback: function() {
Blockly.copy(/** @type {!Blockly.ICopyable} */ (Blockly.selected));
Blockly.clipboard.copy(/** @type {!Blockly.ICopyable} */ (Blockly.selected));
Blockly.deleteBlock(/** @type {!Blockly.BlockSvg} */ (Blockly.selected));
return true;
}
@@ -167,7 +168,7 @@ Blockly.ShortcutItems.registerPaste = function() {
return !workspace.options.readOnly && !Blockly.Gesture.inProgress();
},
callback: function() {
return Blockly.paste();
return Blockly.clipboard.paste();
}
};

View File

@@ -12,11 +12,12 @@ goog.addDependency('../../core/block_animations.js', ['Blockly.blockAnimations']
goog.addDependency('../../core/block_drag_surface.js', ['Blockly.BlockDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/block_dragger.js', ['Blockly.BlockDragger'], ['Blockly.Events', 'Blockly.Events.BlockDrag', 'Blockly.Events.BlockMove', 'Blockly.IBlockDragger', 'Blockly.InsertionMarkerManager', 'Blockly.blockAnimations', 'Blockly.registry', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/block_svg.js', ['Blockly.BlockSvg'], ['Blockly.ASTNode', 'Blockly.Block', 'Blockly.ContextMenu', 'Blockly.ContextMenuRegistry', 'Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Selected', 'Blockly.IASTNodeLocationSvg', 'Blockly.IBoundedElement', 'Blockly.ICopyable', 'Blockly.IDraggable', 'Blockly.Msg', 'Blockly.RenderedConnection', 'Blockly.TabNavigateCursor', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Xml', 'Blockly.blockAnimations', 'Blockly.blockRendering.IPathObject', 'Blockly.browserEvents', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent']);
goog.addDependency('../../core/blockly.js', ['Blockly'], ['Blockly.ComponentManager', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.Ui', 'Blockly.Events.UiBase', 'Blockly.Events.VarCreate', 'Blockly.Procedures', 'Blockly.ShortcutRegistry', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Variables', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.browserEvents', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.inject', 'Blockly.inputTypes', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.colour', 'Blockly.utils.deprecation', 'Blockly.utils.toolbox']);
goog.addDependency('../../core/blockly.js', ['Blockly'], ['Blockly.ComponentManager', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.Ui', 'Blockly.Events.UiBase', 'Blockly.Events.VarCreate', 'Blockly.Procedures', 'Blockly.ShortcutRegistry', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Variables', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.browserEvents', 'Blockly.clipboard', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.inject', 'Blockly.inputTypes', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.colour', 'Blockly.utils.deprecation', 'Blockly.utils.toolbox']);
goog.addDependency('../../core/blocks.js', ['Blockly.Blocks'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/browser_events.js', ['Blockly.browserEvents'], ['Blockly.Touch', 'Blockly.utils.global']);
goog.addDependency('../../core/bubble.js', ['Blockly.Bubble'], ['Blockly.IBubble', 'Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.Workspace', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.userAgent']);
goog.addDependency('../../core/bubble_dragger.js', ['Blockly.BubbleDragger'], ['Blockly.Bubble', 'Blockly.ComponentManager', 'Blockly.Events', 'Blockly.Events.CommentMove', 'Blockly.constants', 'Blockly.utils', 'Blockly.utils.Coordinate'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/clipboard.js', ['Blockly.clipboard'], ['Blockly.Events'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/comment.js', ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BubbleOpen', 'Blockly.Icon', 'Blockly.Warning', 'Blockly.browserEvents', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/component_manager.js', ['Blockly.ComponentManager'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/connection.js', ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.IASTNodeLocationWithBlock', 'Blockly.Xml', 'Blockly.connectionTypes', 'Blockly.constants', 'Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'});
@@ -25,7 +26,7 @@ goog.addDependency('../../core/connection_db.js', ['Blockly.ConnectionDB'], ['Bl
goog.addDependency('../../core/connection_types.js', ['Blockly.connectionTypes'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/constants.js', ['Blockly.constants'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/contextmenu.js', ['Blockly.ContextMenu'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.Msg', 'Blockly.WidgetDiv', 'Blockly.Xml', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.userAgent']);
goog.addDependency('../../core/contextmenu_items.js', ['Blockly.ContextMenuItems'], ['Blockly.ContextMenuRegistry', 'Blockly.Events', 'Blockly.constants', 'Blockly.inputTypes'], {'lang': 'es5'});
goog.addDependency('../../core/contextmenu_items.js', ['Blockly.ContextMenuItems'], ['Blockly.ContextMenuRegistry', 'Blockly.Events', 'Blockly.clipboard', 'Blockly.constants', 'Blockly.inputTypes'], {'lang': 'es5'});
goog.addDependency('../../core/contextmenu_registry.js', ['Blockly.ContextMenuRegistry'], [], {'lang': 'es5'});
goog.addDependency('../../core/css.js', ['Blockly.Css'], [], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/delete_area.js', ['Blockly.DeleteArea'], ['Blockly.BlockSvg', 'Blockly.DragTarget', 'Blockly.IDeleteArea', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
@@ -159,7 +160,7 @@ goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.
goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.connectionTypes', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo']);
goog.addDependency('../../core/requires.js', ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.ContextMenuItems', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.HorizontalFlyout', 'Blockly.Mutator', 'Blockly.ShortcutItems', 'Blockly.Themes.Classic', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.ZoomControls', 'Blockly.geras.Renderer', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer']);
goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Events', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', 'Blockly.utils.Svg', 'Blockly.utils.dom']);
goog.addDependency('../../core/shortcut_items.js', ['Blockly.ShortcutItems'], ['Blockly.Gesture', 'Blockly.ShortcutRegistry', 'Blockly.utils.KeyCodes']);
goog.addDependency('../../core/shortcut_items.js', ['Blockly.ShortcutItems'], ['Blockly.Gesture', 'Blockly.ShortcutRegistry', 'Blockly.clipboard', 'Blockly.utils.KeyCodes']);
goog.addDependency('../../core/shortcut_registry.js', ['Blockly.ShortcutRegistry'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/theme.js', ['Blockly.Theme'], ['Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'});
goog.addDependency('../../core/theme/classic.js', ['Blockly.Themes.Classic'], ['Blockly.Theme']);

View File

@@ -17,11 +17,11 @@ suite('Context Menu Items', function() {
Blockly.ContextMenuItems.registerDefaultOptions();
this.registry = Blockly.ContextMenuRegistry.registry;
});
teardown(function() {
sharedTestTeardown.call(this);
});
suite('Workspace Items', function() {
setup(function() {
this.scope = {workspace: this.workspace};
@@ -328,12 +328,12 @@ suite('Context Menu Items', function() {
});
test('Calls duplicate', function() {
var stub = sinon.stub(Blockly, 'duplicate');
var spy = sinon.spy(Blockly.clipboard, 'duplicate');
this.duplicateOption.callback(this.scope);
sinon.assert.calledOnce(stub);
sinon.assert.calledWith(stub, this.block);
sinon.assert.calledOnce(spy);
sinon.assert.calledWith(spy, this.block);
});
test('Has correct label', function() {

View File

@@ -93,7 +93,7 @@ suite('Key Down', function() {
suite('Copy', function() {
setup(function() {
setSelectedBlock(this.workspace);
this.copySpy = sinon.spy(Blockly, 'copy');
this.copySpy = sinon.spy(Blockly.clipboard, 'copy');
this.hideChaffSpy = sinon.spy(Blockly, 'hideChaff');
});
var testCases = [