Skip to content

refactor!: Move responsibility for block creation out of flyouts#9610

Open
gonfunko wants to merge 1 commit intov13from
flyout-moves
Open

refactor!: Move responsibility for block creation out of flyouts#9610
gonfunko wants to merge 1 commit intov13from
flyout-moves

Conversation

@gonfunko
Copy link
Contributor

@gonfunko gonfunko commented Mar 3, 2026

The basics

The details

Proposed Changes

This PR moves block creation out of the flyout and into the BlockDragStrategy. This simplifies the code overall and results in a better separation of concerns: flyouts are capable of holding arbitrary kinds of objects now, so having them tightly coupled to blocks is suboptimal. Now, duplicating blocks when dragged out of the flyout is handled by the BlockDragStrategy, and the flyout is concerned only with the display and layout of its contents, whatever they may be. As a result of this change, it's now possible to copy blocks out of the flyout using the keyboard alone by selecting a block in the flyout and hitting m to enter move mode, which copies the block to the main workspace and puts it in move mode, exactly the same as a mouse-driven drag would.

Breaking Changes

  • createBlock(), isDragTowardWorkspace(), and isBlockCreatable() have been removed from IFlyout. While this is not breaking from a building standpoint, it is behaviorally breaking if you have an alternate flyout implementation that customized those methods. isDragTowardWorkspace() was already a no-op, so no action needs to be taken other than removing it. Customizations that were made to createBlock() and isBlockCreatable() can be achieved by creating a BlockDragStrategy subclass that overrides isMovable() and/or swapTargetBlock() and using a mixin or extension to apply that drag strategy to your blocks.

@gonfunko gonfunko requested a review from maribethb March 3, 2026 20:42
@gonfunko gonfunko requested a review from a team as a code owner March 3, 2026 20:42
@github-actions github-actions bot added breaking change Used to mark a PR or issue that changes our public APIs. PR: refactor labels Mar 3, 2026
@gonfunko
Copy link
Contributor Author

gonfunko commented Mar 3, 2026

* from any parent blocks.
*/
startDrag(e?: PointerEvent | KeyboardEvent) {
const alterateTarget = this.swapTargetBlock();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: alternate (if this stays)

* Returns a block to use for the current drag operation in place of the
* current block, or undefined if the current block should be used.
*/
protected swapTargetBlock() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we discussed offline why this is the way it is:

startDrag on the dragger returns the thing being dragged, to allow for cases just like this. since the block drag strategy is the thing that ultimately decides which thing is being dragged, it has to have a method for doing that logic.

we decided to call it getTargetBlock (or something similar) which feels a little simpler and more generic. It might be a little unexpected that a get method can create a new block (as it does in the flyout case) so should be explained in the tsdoc.

getFocusManager().focusNode(newBlock);
}

// Perform a zero-length drag to copy the block out of the flyout.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really weird that Gesture does this I think. A couple of thoughts:

  • I know some people were doing custom block creation shenanigans where they would have a block look different in the flyout than it did in the main workspace, to make the flyout look simpler. I don't know how this works, are they doing it with fancy block definitions (that change based on whether the block is in a flyout) or were they doing it with fancy flyouts that override flyout.createBlock? If the latter, now they'll have to override the drag strategy instead which is maybe unexpected. Is there a place, maybe on the block class which can be customized with a mixin, where we can have the logic for "create a new block of this type from the flyout"?
  • If we did have that, it could also be called from the keyboard shortcut that handles pressing "enter" on a block in the flyout (which currently calls flyout.createBlock as well)
  • otherwise, the Enter shortcut is also going to have to create one of these fake drags which is also undesirable.
  • Then, as to the fact that gesture is for some reason handling creating a block on click, maybe blocks should just do this themselves in response to a block click? if they're on a flyout and get a click event, then they could clone themselves onto the main workspace without involving the dragger at all? I know you're just recreating the logic that was here but the old logic was weird too.

Let me know your thoughts here!

test('Clicking on shadow block does not select it', function () {
const flyout = this.workspace.getFlyout(true);
flyout.createBlock(
Blockly.KeyboardMover.mover.startMove(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just a convoluted way to get a block with a shadow block onto the workspace? why not do that via serializing a block instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Used to mark a PR or issue that changes our public APIs. PR: refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants