Moves the cursor to correct location when block is deleted (#2887)

* Moves the cursor to correct location when block is deleted

* Moves cursor on block mutation
This commit is contained in:
alschmiedt
2019-08-21 16:33:16 -07:00
committed by GitHub
parent 33e6fa6ed9
commit 7bba4fa59c
6 changed files with 150 additions and 13 deletions

View File

@@ -287,6 +287,12 @@ Blockly.Block.prototype.dispose = function(healStack) {
if (this.onchangeWrapper_) {
this.workspace.removeChangeListener(this.onchangeWrapper_);
}
if (Blockly.keyboardAccessibilityMode) {
// No-op if this is called from the block_svg class.
Blockly.navigation.moveCursorOnBlockDelete(this);
}
this.unplug(healStack);
if (Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.BlockDelete(this));

View File

@@ -907,6 +907,10 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
Blockly.ContextMenu.hide();
}
if (Blockly.keyboardAccessibilityMode) {
Blockly.navigation.moveCursorOnBlockDelete(this);
}
if (animate && this.rendered) {
this.unplug(healStack);
Blockly.blockAnimations.disposeUiEffect(this);

View File

@@ -138,18 +138,12 @@ Blockly.CursorSvg.prototype.setParent_ = function(newParent) {
if (newParent == oldParent) {
return;
}
var svgRoot = this.getSvgRoot();
if (newParent) {
newParent.appendChild(svgRoot);
this.parent_ = newParent;
}
// If we are losing a parent, we want to move our DOM element to the
// root of the workspace.
else if (oldParent) {
this.workspace_.getCanvas().appendChild(svgRoot);
}
this.parent_ = newParent;
};
/**************************/

View File

@@ -662,6 +662,79 @@ Blockly.navigation.handleEnterForWS = function() {
/** Helper Functions **/
/**********************/
/**
* Finds the source block of the location on a given node.
* @param {Blockly.ASTNode} node The node to find the source block on.
* @return {Blockly.Block} The source block of the location on the given node,
* or null if the node is of type workspace.
* @private
*/
Blockly.navigation.getSourceBlock_ = function(node) {
if (!node) {
return null;
}
if (node.getType() === Blockly.ASTNode.types.BLOCK) {
return node.getLocation();
} else if (node.getType() === Blockly.ASTNode.types.WORKSPACE ||
node.getType() === Blockly.ASTNode.types.STACK) {
return null;
} else {
return node.getLocation().getSourceBlock();
}
};
/**
* Before a block is deleted move the cursor to the appropriate position.
* @param {!Blockly.Block} deletedBlock The block that is being deleted.
* @package
*/
Blockly.navigation.moveCursorOnBlockDelete = function(deletedBlock) {
var cursor = Blockly.navigation.cursor_;
if (cursor) {
var curNode = cursor.getCurNode();
var block = Blockly.navigation.getSourceBlock_(curNode);
if (block === deletedBlock) {
// If the block has a parent move the cursor to their connection point.
if (block.getParent()) {
var topConnection = block.previousConnection ?
block.previousConnection : block.outputConnection;
if (topConnection) {
cursor.setLocation(
Blockly.ASTNode.createConnectionNode(topConnection.targetConnection));
}
} else {
// If the block is by itself move the cursor to the workspace.
cursor.setLocation(Blockly.ASTNode.createWorkspaceNode(block.workspace,
block.getRelativeToSurfaceXY()));
}
// If the cursor is on a block whose parent is being deleted, move the
// cursor to the workspace.
} else if (deletedBlock.getChildren().indexOf(block) > -1) {
cursor.setLocation(Blockly.ASTNode.createWorkspaceNode(block.workspace,
block.getRelativeToSurfaceXY()));
}
}
};
/**
* When a block that the cursor is on is mutated move the cursor to the block
* level.
* @param {!Blockly.Block} mutatedBlock The block that is being mutated.
* @package
*/
Blockly.navigation.moveCursorOnBlockMutation = function(mutatedBlock) {
var cursor = Blockly.navigation.cursor_;
if (cursor) {
var curNode = cursor.getCurNode();
var block = Blockly.navigation.getSourceBlock_(curNode);
if (block === mutatedBlock) {
cursor.setLocation(Blockly.ASTNode.createBlockNode(block));
}
}
};
/**
* Handler for all the keyboard navigation events.
* @param {Event} e The keyboard event.

View File

@@ -373,6 +373,10 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
if (block.rendered) {
block.render();
}
if (oldMutation != newMutation && Blockly.keyboardAccessibilityMode) {
Blockly.navigation.moveCursorOnBlockMutation(block);
}
// Don't update the bubble until the drag has ended, to avoid moving blocks
// under the cursor.
if (!this.workspace_.isDragging()) {

View File

@@ -94,7 +94,8 @@ suite('Navigation', function() {
chai.assert.equal(Blockly.navigation.currentState_,
Blockly.navigation.STATE_FLYOUT);
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), "FirstCategory-FirstBlock");
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"),
"FirstCategory-FirstBlock");
});
test('Focuses workspace from toolbox (e)', function() {
@@ -153,17 +154,20 @@ suite('Navigation', function() {
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
chai.assert.equal(Blockly.navigation.currentState_,
Blockly.navigation.STATE_FLYOUT);
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), "FirstCategory-FirstBlock");
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"),
"FirstCategory-FirstBlock");
});
test('Previous', function() {
Blockly.navigation.selectNextBlockInFlyout();
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), "FirstCategory-SecondBlock");
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"),
"FirstCategory-SecondBlock");
this.mockEvent.keyCode = Blockly.utils.KeyCodes.W;
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
chai.assert.equal(Blockly.navigation.currentState_,
Blockly.navigation.STATE_FLYOUT);
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), "FirstCategory-FirstBlock");
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"),
"FirstCategory-FirstBlock");
});
test('Next', function() {
@@ -171,7 +175,8 @@ suite('Navigation', function() {
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
chai.assert.equal(Blockly.navigation.currentState_,
Blockly.navigation.STATE_FLYOUT);
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"), "FirstCategory-SecondBlock");
chai.assert.equal(Blockly.navigation.flyoutBlock_.getFieldValue("TEXT"),
"FirstCategory-SecondBlock");
});
test('Out', function() {
@@ -386,6 +391,12 @@ suite('Navigation', function() {
this.basicBlock2 = basicBlock2;
});
teardown(function() {
delete Blockly.Blocks['basic_block'];
this.workspace.dispose();
Blockly.navigation.currentCategory_ = null;
});
test('Insert from flyout with a valid connection marked', function() {
var previousConnection = this.basicBlock.previousConnection;
var prevNode = Blockly.ASTNode.createConnectionNode(previousConnection);
@@ -431,12 +442,57 @@ suite('Navigation', function() {
chai.assert.isNotNull(insertedBlock);
});
});
suite('Test cursor move on block delete', function() {
setup(function() {
Blockly.defineBlocksWithJsonArray([{
"type": "basic_block",
"message0": "",
"previousStatement": null,
"nextStatement": null,
}]);
var toolbox = document.getElementById('toolbox-categories');
this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
Blockly.navigation.focusWorkspace();
this.basicBlockA = this.workspace.newBlock('basic_block');
this.basicBlockB = this.workspace.newBlock('basic_block');
Blockly.keyboardAccessibilityMode = true;
});
teardown(function() {
delete Blockly.Blocks['basic_block'];
this.workspace.dispose();
Blockly.navigation.currentCategory_ = null;
});
test('Delete block - has parent ', function() {
this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection);
var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB);
// Set the cursor to be on the child block
this.workspace.cursor.setLocation(astNode);
// Remove the child block
this.basicBlockB.dispose();
chai.assert.equal(this.workspace.cursor.getCurNode().getType(),
Blockly.ASTNode.types.NEXT);
});
test('Delete block - no parent ', function() {
var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB);
this.workspace.cursor.setLocation(astNode);
this.basicBlockB.dispose();
chai.assert.equal(this.workspace.cursor.getCurNode().getType(),
Blockly.ASTNode.types.WORKSPACE);
});
test('Delete parent block', function() {
this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection);
var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB);
// Set the cursor to be on the child block
this.workspace.cursor.setLocation(astNode);
// Remove the parent block
this.basicBlockA.dispose();
chai.assert.equal(this.workspace.cursor.getCurNode().getType(),
Blockly.ASTNode.types.WORKSPACE);
});
});
});