A draggable is a rendered object that exists in the
workspace and can be dragged and dropped. They
implement the IDraggable
interface.
There are very few circumstances when you would want to add a new draggable to Blockly (e.g. the multiselect plugin, or changing how an existing object handles drags), because you cannot add new rendered objects to Blockly. The only rendered objects that can exist within a workspace are blocks, bubbles, and workspace comments.
Responsibilities
Draggables have several responsibilities when executing drags:
- Moving svg elements to the drag layer.
- Translating svg elements.
- Firing move events.
Implementation
To create a new draggable, you have to implement the
IRenderedElement
and IDraggable
interfaces. This lets Blockly know that your object is visible and can
be dragged.
class MyDraggable extends IRenderedElement, IDraggable {}
Return the root SVG element
The getRootSvg
method returns the root svg element (usually a
group) that holds all of the other elements making up the view for
the draggable.
getSvgRoot() {
return this.rootSvg;
}
Return movability
The isMovable
method returns whether the draggable is currently movable (since
you may want to temporarily disable dragging an object). If isMovable
returns
false
the workspace is dragged instead.
isMovable() {
return true;
}
Return position
The getRelativeToSurfaceXY
method returns a Coordinate
specifying the location of the top-start corner of the draggable in workspace
coordinates.
Workspace coordinates have an origin at the absolute left and absolute top of the workspace. And they don't change when the workspace is scaled or moved.
getRelativeToSurfaceXY() {
return this.loc;
}
Start drags
The startDrag
method initializes a drag on the draggable. This method doesn't
move the draggable. But you should store any data or construct any objects you
need to complete the drag. This includes any data you would need to revert the
drag if revertDrag
is called.
It should also change the svg elements to be on the drag layer of the workspace, so they exist above any other elements.
It also takes in an event, which you can use to check for pressed keys. This lets you (for example) treat a drag while shifting differently than a normal drag.
startDrag(e) {
// Save the original location so we can revert the drag.
this.startLoc = this.getRelativeToSurfaceXY();
// Disable resizing the workspace for performance.
this.workspace.setResizesEnabled(false);
// Put the element on the drag layer.
this.workspace.getLayerManager()?.moveToDragLayer(this);
// Fire a drag event...
// etc...
}
Drag
The drag
method actually moves the draggable object. The newLoc
is in
workspace coordinates, and there is also an event passed that you can use to
check for pressed keys.
drag(newLoc, e) {
this.moveTo(newLoc);
}
Revert drags
The revertDrag
method returns the draggable to the position it was at at the
beginning of the drag. This happens if, for example, the draggable is dropped on
a drag target that prevents movement.
revertDrag() {
// Move back to the position that was stored in `startDrag`.
this.moveTo(this.startLoc);
}
End drags
The endDrag
method cleans up a drag, releasing any stored data or objects, and
returning the draggable to its original layer.
endDrag
is always called after revertDrag
if revertDrag
is called.
endDrag() {
// Put the element back on its original layer (in this case BLOCK).
this.workspace
.getLayerManager()
?.moveOffDragLayer(this, Blockly.layers.BLOCK);
// Allow the workspace to start resizing again.
this.workspace.setResizesEnabled(true);
}
Selection
The element that gets dragged is determined by the element that is selected when a drag is detected.
ISelectable
To be selected a draggable must implement the ISelectable
interface.
class MyDraggable implements ISelectable {
constructor(workspace) {
this.id = Blockly.utils.idGenerator.genUid();
this.workspace = workspace;
}
select() {
// Visually indicate this draggable is selected.
}
unselect() {
// Visually indicate this draggable is not selected.
}
}
Set selection
The selected element can be set by calling
Blockly.common.setSelected()
. usually you'll want to do this
in response to a pointerdown
event from the user.
constructor() {
this.initSvg();
Blockly.browserEvents.conditionalBind(
this.getSvgRoot(),
'pointerdown',
this,
() => Blockly.common.setSelected(this));
}
Compatibility
Your draggable can implement additional interfaces so that it can interact with other systems in Blockly.
Deletable
You can implement the IDeleteable
interface to allow your
draggable to be disposed by the trashcan or other delete targets.
class MyDraggable implements IDeletable {
isDeletable() {
return true;
}
dispose() {
// Dispose of any references to data or SVG elements.
}
setDeleteStyle() {
// Visually indicate that the draggable is about to be deleted.
}
}
Copyable
You can implement the ICopyable
interface to allow your draggable to be copied
and define an IPaster
to allow it to be pasted.
For more information about copying pasting see Copy paste.