'Duplicate all' ?

347 views
Skip to first unread message

Alex Lynch

unread,
Oct 6, 2016, 12:44:28 PM10/6/16
to Blockly
Hi there I am using blockly and find that a 'Duplicate All' feature would be useful. Currently I have to select each block in a list, duplicate it, then connect it to the new dup of it's parent. This is tedious for large lists.

If I am to implement this feature, I think I would need to do the following:

1. Add a 'duplicate all' option to the right click menu.
2. on click, get the focused block, and then find the root block (where parent = null)
3. duplicate the root block as normal.
4. Then iterate each of the blocks children, and duplicate them also.
5. After each duplication, connect the dup to the dupped parent.

Would this be the best way? And could anyone point me to the files and possible code references I would need before I go flailing around?

Rachel Fenichel

unread,
Oct 6, 2016, 2:38:14 PM10/6/16
to Blockly
Serializing to XML and then deserializing would give you an identical stack without needing to directly iterate through the whole list.

Alex Lynch

unread,
Oct 6, 2016, 2:54:55 PM10/6/16
to Blockly
That's a neat idea, what about the block IDs though? Would they need regenerating to stay unique? Or would that automatically happen?

Rachel Fenichel

unread,
Oct 6, 2016, 3:36:42 PM10/6/16
to Blockly
Happens automatically--if there are already blocks with those IDs, the new blocks will get new IDs.
Message has been deleted

Alex Lynch

unread,
Oct 6, 2016, 3:56:08 PM10/6/16
to Blockly
Ok excellent, so the new steps would be:

1. Add a 'duplicate all' option to the right click menu.
2. on click, get the focused block, and then find the root block (where parent = null)
3. serialise the root block to xml (assuming there is a block.Serialise() function?)
4. Then deserialise the xml back to the workspace (using Blockly.Xml.domToWorkspace())

I'll try writing this tomorrow.

Neil Fraser

unread,
Oct 6, 2016, 6:01:15 PM10/6/16
to blo...@googlegroups.com
On 6 October 2016 at 12:56, Alex Lynch <linch...@gmail.com> wrote:
2. on click, get the focused block, and then find the root block (where parent = null)

var rootBlock = block.getRootBlock();

3. serialise the root block to xml (assuming there is a block.Serialise() function?)

var xml = Blockly.Xml.blockToDom(rootBlock);

4. Then deserialise the xml back to the workspace (using Blockly.Xml.domToWorkspace())

Since you are serializing a block, not a workspace, you want:
var newBlock = Blockly.Xml.domToBlock(xml, workspace);

The only problem is that the copy will appear at coordinates 0,0, which is not optimal.  You should move the copy to just below and off to one side of the original.  I've found that increasing x by Blockly.SNAP_RADIUS, and increasing y bi 2 * Blockly.SNAP_RADIUS produces the best result.  Of course if you are supporting RTL, x needs to be decremented by Blockly.SNAP_RADIUS instead.

Here's how to do this:
var xy = rootBlock.getRelativeToSurfaceXY();
newBlock.moveBy(xy.x + Blockly.SNAP_RADIUS,
    xy.y + Blockly.SNAP_RADIUS * (newBlock.RTL ? -2 : 2));


--

Alex Lynch

unread,
Oct 7, 2016, 5:33:39 AM10/7/16
to Blockly, ro...@neil.fraser.name
Thanks for that Neil, worked perfectly. The final code:

block_svg.js:
/**
 * Show the context menu for this block.
 * @param {!Event} e Mouse event.
 * @private
 */
Blockly.BlockSvg.prototype.showContextMenu_ = function(e) {
  if (this.workspace.options.readOnly || !this.contextMenu) {
    return;
  }
  // Save the current block in a variable for use in closures.
  var block = this;
  var menuOptions = [];

  if (this.isDeletable() && this.isMovable() && !block.isInFlyout) {
    // Option to duplicate this block.
    var duplicateOption = {
      text: Blockly.Msg.DUPLICATE_BLOCK,
      enabled: true,
      callback: function() {
        Blockly.duplicate_(block);
      }
    }; 
    var duplicateSetOption = {
      text: Blockly.Msg.DUPLICATE_SET_BLOCK,
      enabled: true,
      callback: function() {
        Blockly.duplicateSet_(block);
      }
    };
    if (this.getDescendants().length > this.workspace.remainingCapacity()) {
      duplicateOption.enabled = false;
      duplicateSetOption.enabled = false;
    }
    menuOptions.push(duplicateOption);
    menuOptions.push(duplicateSetOption);
    ....

 blockly.js:
/**
 * Duplicate from the block's root and all it's connections.
 * @param {!Blockly.Block} block Block to be copied.
 * @private
 */
Blockly.duplicateSet_ = function(block) {

  // get root
  var rootBlock = block.getRootBlock();

  // serialise
  var xml = Blockly.Xml.blockToDom(rootBlock);

  // deserialise
  var newBlock = Blockly.Xml.domToBlock(xml, block.workspace);

  // move above the original block
};


Reply all
Reply to author
Forward
0 new messages