mirror of
https://github.com/google/blockly.git
synced 2026-01-04 15:40:08 +01:00
fix: Fix blocks with mutators. (#6440)
* refactor: Revert the Mutator/Icon constructor API changes with a deprecation warning. * fix: Update the block definitions to use the new Mutator constructor.
This commit is contained in:
@@ -133,7 +133,7 @@ blocks['lists_create_with'] = {
|
||||
this.itemCount_ = 3;
|
||||
this.updateShape_();
|
||||
this.setOutput(true, 'Array');
|
||||
this.setMutator(new Mutator(['lists_create_with_item']));
|
||||
this.setMutator(new Mutator(['lists_create_with_item'], this));
|
||||
this.setTooltip(Msg['LISTS_CREATE_WITH_TOOLTIP']);
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -461,7 +461,7 @@ blocks['procedures_defnoreturn'] = {
|
||||
.appendField(Msg['PROCEDURES_DEFNORETURN_TITLE'])
|
||||
.appendField(nameField, 'NAME')
|
||||
.appendField('', 'PARAMS');
|
||||
this.setMutator(new Mutator(['procedures_mutatorarg']));
|
||||
this.setMutator(new Mutator(['procedures_mutatorarg'], this));
|
||||
if ((this.workspace.options.comments ||
|
||||
(this.workspace.options.parentWorkspace &&
|
||||
this.workspace.options.parentWorkspace.options.comments)) &&
|
||||
@@ -507,7 +507,7 @@ blocks['procedures_defreturn'] = {
|
||||
this.appendValueInput('RETURN')
|
||||
.setAlign(Align.RIGHT)
|
||||
.appendField(Msg['PROCEDURES_DEFRETURN_RETURN']);
|
||||
this.setMutator(new Mutator(['procedures_mutatorarg']));
|
||||
this.setMutator(new Mutator(['procedures_mutatorarg'], this));
|
||||
if ((this.workspace.options.comments ||
|
||||
(this.workspace.options.parentWorkspace &&
|
||||
this.workspace.options.parentWorkspace.options.comments)) &&
|
||||
|
||||
@@ -864,7 +864,7 @@ const TEXT_JOIN_EXTENSION = function() {
|
||||
this.itemCount_ = 2;
|
||||
this.updateShape_();
|
||||
// Configure the mutator UI.
|
||||
this.setMutator(new Mutator(['text_create_join_item']));
|
||||
this.setMutator(new Mutator(['text_create_join_item'], this));
|
||||
};
|
||||
|
||||
// Update the tooltip of 'text_append' block to reference the variable.
|
||||
|
||||
@@ -142,7 +142,7 @@ export class Comment extends Icon {
|
||||
HTMLTextAreaElement;
|
||||
const textarea = this.textarea_;
|
||||
textarea.className = 'blocklyCommentTextarea';
|
||||
textarea.setAttribute('dir', this.block_.RTL ? 'RTL' : 'LTR');
|
||||
textarea.setAttribute('dir', this.getBlock().RTL ? 'RTL' : 'LTR');
|
||||
textarea.value = this.model_.text ?? '';
|
||||
this.resizeTextarea_();
|
||||
|
||||
@@ -167,7 +167,7 @@ export class Comment extends Icon {
|
||||
function(this: Comment, _e: Event) {
|
||||
if (this.cachedText_ !== this.model_.text) {
|
||||
eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))(
|
||||
this.block_, 'comment', null, this.cachedText_,
|
||||
this.getBlock(), 'comment', null, this.cachedText_,
|
||||
this.model_.text));
|
||||
}
|
||||
});
|
||||
@@ -233,7 +233,7 @@ export class Comment extends Icon {
|
||||
return;
|
||||
}
|
||||
eventUtils.fire(new (eventUtils.get(eventUtils.BUBBLE_OPEN))(
|
||||
this.block_, visible, 'comment'));
|
||||
this.getBlock(), visible, 'comment'));
|
||||
this.model_.pinned = visible;
|
||||
if (visible) {
|
||||
this.createBubble_();
|
||||
@@ -244,7 +244,7 @@ export class Comment extends Icon {
|
||||
|
||||
/** Show the bubble. Handles deciding if it should be editable or not. */
|
||||
private createBubble_() {
|
||||
if (!this.block_.isEditable()) {
|
||||
if (!this.getBlock().isEditable()) {
|
||||
this.createNonEditableBubble_();
|
||||
} else {
|
||||
this.createEditableBubble_();
|
||||
@@ -253,12 +253,13 @@ export class Comment extends Icon {
|
||||
|
||||
/** Show an editable bubble. */
|
||||
private createEditableBubble_() {
|
||||
const block = this.getBlock();
|
||||
this.bubble_ = new Bubble(
|
||||
this.block_.workspace, this.createEditor_(),
|
||||
this.block_.pathObject.svgPath, (this.iconXY_ as Coordinate),
|
||||
this.model_.size.width, this.model_.size.height);
|
||||
block.workspace, this.createEditor_(), block.pathObject.svgPath,
|
||||
(this.iconXY_ as Coordinate), this.model_.size.width,
|
||||
this.model_.size.height);
|
||||
// Expose this comment's block's ID on its top-level SVG group.
|
||||
this.bubble_.setSvgId(this.block_.id);
|
||||
this.bubble_.setSvgId(block.id);
|
||||
this.bubble_.registerResizeEvent(this.onBubbleResize_.bind(this));
|
||||
this.applyColour();
|
||||
}
|
||||
@@ -272,7 +273,7 @@ export class Comment extends Icon {
|
||||
// TODO (#2917): It would be great if the comment could support line breaks.
|
||||
this.paragraphElement_ = Bubble.textToDom(this.model_.text ?? '');
|
||||
this.bubble_ = Bubble.createNonEditableBubble(
|
||||
this.paragraphElement_, (this.block_), this.iconXY_ as Coordinate);
|
||||
this.paragraphElement_, this.getBlock(), this.iconXY_ as Coordinate);
|
||||
this.applyColour();
|
||||
}
|
||||
|
||||
@@ -371,7 +372,7 @@ export class Comment extends Icon {
|
||||
* should not be called directly. Instead call block.setCommentText(null);
|
||||
*/
|
||||
override dispose() {
|
||||
this.block_.comment = null;
|
||||
this.getBlock().comment = null;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ export function registerMutator(
|
||||
// Sanity checks passed.
|
||||
register(name, function(this: Block) {
|
||||
if (hasMutatorDialog) {
|
||||
this.setMutator(new Mutator(this as BlockSvg, opt_blockList || []));
|
||||
this.setMutator(new Mutator(opt_blockList || [], this as BlockSvg));
|
||||
}
|
||||
// Mixin the object.
|
||||
this.mixin(mixinObj);
|
||||
|
||||
39
core/icon.ts
39
core/icon.ts
@@ -20,6 +20,7 @@ import * as dom from './utils/dom.js';
|
||||
import {Size} from './utils/size.js';
|
||||
import {Svg} from './utils/svg.js';
|
||||
import * as svgMath from './utils/svg_math.js';
|
||||
import * as deprecation from './utils/deprecation.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -28,7 +29,7 @@ import * as svgMath from './utils/svg_math.js';
|
||||
* @alias Blockly.Icon
|
||||
*/
|
||||
export abstract class Icon {
|
||||
protected block_: BlockSvg;
|
||||
protected block_: BlockSvg|null;
|
||||
/** The icon SVG group. */
|
||||
iconGroup_: SVGGElement|null = null;
|
||||
|
||||
@@ -45,7 +46,12 @@ export abstract class Icon {
|
||||
protected iconXY_: Coordinate|null = null;
|
||||
|
||||
/** @param block The block associated with this icon. */
|
||||
constructor(block: BlockSvg) {
|
||||
constructor(block: BlockSvg|null) {
|
||||
if (!block) {
|
||||
deprecation.warn(
|
||||
'Calling the Icon constructor with a null block', 'version 9',
|
||||
'version 10', 'a non-null block');
|
||||
}
|
||||
this.block_ = block;
|
||||
}
|
||||
|
||||
@@ -62,12 +68,12 @@ export abstract class Icon {
|
||||
*/
|
||||
this.iconGroup_ =
|
||||
dom.createSvgElement(Svg.G, {'class': 'blocklyIconGroup'});
|
||||
if (this.block_.isInFlyout) {
|
||||
if (this.getBlock().isInFlyout) {
|
||||
this.iconGroup_.classList.add('blocklyIconGroupReadonly');
|
||||
}
|
||||
this.drawIcon_(this.iconGroup_);
|
||||
|
||||
this.block_.getSvgRoot().appendChild(this.iconGroup_);
|
||||
this.getBlock().getSvgRoot().appendChild(this.iconGroup_);
|
||||
browserEvents.conditionalBind(
|
||||
this.iconGroup_, 'mouseup', this, this.iconClick_);
|
||||
this.updateEditable();
|
||||
@@ -99,19 +105,19 @@ export abstract class Icon {
|
||||
* @param e Mouse click event.
|
||||
*/
|
||||
protected iconClick_(e: MouseEvent) {
|
||||
if (this.block_.workspace.isDragging()) {
|
||||
if (this.getBlock().workspace.isDragging()) {
|
||||
// Drag operation is concluding. Don't open the editor.
|
||||
return;
|
||||
}
|
||||
if (!this.block_.isInFlyout && !browserEvents.isRightButton(e)) {
|
||||
if (!this.getBlock().isInFlyout && !browserEvents.isRightButton(e)) {
|
||||
this.setVisible(!this.isVisible());
|
||||
}
|
||||
}
|
||||
|
||||
/** Change the colour of the associated bubble to match its block. */
|
||||
applyColour() {
|
||||
if (this.isVisible()) {
|
||||
this.bubble_!.setColour(this.block_.style.colourPrimary);
|
||||
if (this.bubble_ && this.isVisible()) {
|
||||
this.bubble_.setColour(this.getBlock().style.colourPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +128,8 @@ export abstract class Icon {
|
||||
*/
|
||||
setIconLocation(xy: Coordinate) {
|
||||
this.iconXY_ = xy;
|
||||
if (this.isVisible()) {
|
||||
this.bubble_!.setAnchorLocation(xy);
|
||||
if (this.bubble_ && this.isVisible()) {
|
||||
this.bubble_.setAnchorLocation(xy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +139,7 @@ export abstract class Icon {
|
||||
*/
|
||||
computeIconLocation() {
|
||||
// Find coordinates for the centre of the icon and update the arrow.
|
||||
const blockXY = this.block_.getRelativeToSurfaceXY();
|
||||
const blockXY = this.getBlock().getRelativeToSurfaceXY();
|
||||
const iconXY = svgMath.getRelativeXY(this.iconGroup_ as SVGElement);
|
||||
const newXY = new Coordinate(
|
||||
blockXY.x + iconXY.x + this.SIZE / 2,
|
||||
@@ -178,5 +184,16 @@ export abstract class Icon {
|
||||
* @param _visible True if the icon should be visible.
|
||||
*/
|
||||
setVisible(_visible: boolean) {}
|
||||
|
||||
/**
|
||||
* Returns the block this icon is attached to.
|
||||
*/
|
||||
protected getBlock(): BlockSvg {
|
||||
if (!this.block_) {
|
||||
throw new Error('Block is not set for this icon.');
|
||||
}
|
||||
|
||||
return this.block_;
|
||||
}
|
||||
}
|
||||
// No-op on base class
|
||||
|
||||
@@ -32,6 +32,7 @@ import * as dom from './utils/dom.js';
|
||||
import {Svg} from './utils/svg.js';
|
||||
import * as toolbox from './utils/toolbox.js';
|
||||
import * as xml from './utils/xml.js';
|
||||
import * as deprecation from './utils/deprecation.js';
|
||||
import type {WorkspaceSvg} from './workspace_svg.js';
|
||||
|
||||
|
||||
@@ -77,8 +78,14 @@ export class Mutator extends Icon {
|
||||
private updateWorkspacePid_: ReturnType<typeof setTimeout>|null = null;
|
||||
|
||||
/** @param quarkNames List of names of sub-blocks for flyout. */
|
||||
constructor(block: BlockSvg, quarkNames: string[]) {
|
||||
super(block);
|
||||
constructor(quarkNames: string[], block?: BlockSvg) {
|
||||
if (!block) {
|
||||
deprecation.warn(
|
||||
'Calling the Mutator constructor without passing the block it is attached to',
|
||||
'version 9', 'version 10',
|
||||
'the constructor by passing the list of subblocks and the block instance to attach the mutator to');
|
||||
}
|
||||
super(block ?? null);
|
||||
this.quarkNames_ = quarkNames;
|
||||
}
|
||||
|
||||
@@ -145,7 +152,7 @@ export class Mutator extends Icon {
|
||||
* @param e Mouse click event.
|
||||
*/
|
||||
protected override iconClick_(e: MouseEvent) {
|
||||
if (this.block_.isEditable()) {
|
||||
if (this.getBlock().isEditable()) {
|
||||
super.iconClick_(e);
|
||||
}
|
||||
}
|
||||
@@ -175,19 +182,20 @@ export class Mutator extends Icon {
|
||||
} else {
|
||||
quarkXml = null;
|
||||
}
|
||||
const block = this.getBlock();
|
||||
const workspaceOptions = new Options(({
|
||||
// If you want to enable disabling, also remove the
|
||||
// event filter from workspaceChanged_ .
|
||||
'disable': false,
|
||||
'parentWorkspace': this.block_.workspace,
|
||||
'media': this.block_.workspace.options.pathToMedia,
|
||||
'rtl': this.block_.RTL,
|
||||
'parentWorkspace': block.workspace,
|
||||
'media': block.workspace.options.pathToMedia,
|
||||
'rtl': block.RTL,
|
||||
'horizontalLayout': false,
|
||||
'renderer': this.block_.workspace.options.renderer,
|
||||
'rendererOverrides': this.block_.workspace.options.rendererOverrides,
|
||||
'renderer': block.workspace.options.renderer,
|
||||
'rendererOverrides': block.workspace.options.rendererOverrides,
|
||||
} as BlocklyOptions));
|
||||
workspaceOptions.toolboxPosition =
|
||||
this.block_.RTL ? toolbox.Position.RIGHT : toolbox.Position.LEFT;
|
||||
block.RTL ? toolbox.Position.RIGHT : toolbox.Position.LEFT;
|
||||
const hasFlyout = !!quarkXml;
|
||||
if (hasFlyout) {
|
||||
workspaceOptions.languageTree = toolbox.convertToolboxDefToJson(quarkXml);
|
||||
@@ -227,8 +235,8 @@ export class Mutator extends Icon {
|
||||
/** Add or remove the UI indicating if this icon may be clicked or not. */
|
||||
override updateEditable() {
|
||||
super.updateEditable();
|
||||
if (!this.block_.isInFlyout) {
|
||||
if (this.block_.isEditable()) {
|
||||
if (!this.getBlock().isInFlyout) {
|
||||
if (this.getBlock().isEditable()) {
|
||||
if (this.iconGroup_) {
|
||||
this.iconGroup_.classList.remove('blocklyIconGroupReadonly');
|
||||
}
|
||||
@@ -255,7 +263,7 @@ export class Mutator extends Icon {
|
||||
height = Math.max(height, flyoutScrollMetrics.height + 20);
|
||||
width += flyout.getWidth();
|
||||
}
|
||||
if (this.block_.RTL) {
|
||||
if (this.getBlock().RTL) {
|
||||
width = -workspaceSize.x;
|
||||
}
|
||||
width += doubleBorderWidth * 3;
|
||||
@@ -275,7 +283,7 @@ export class Mutator extends Icon {
|
||||
this.workspaceWidth_, this.workspaceHeight_);
|
||||
}
|
||||
|
||||
if (this.block_.RTL) {
|
||||
if (this.getBlock().RTL) {
|
||||
// Scroll the workspace to always left-align.
|
||||
const translation = 'translate(' + this.workspaceWidth_ + ',0)';
|
||||
this.workspace_!.getCanvas().setAttribute('transform', translation);
|
||||
@@ -300,16 +308,16 @@ export class Mutator extends Icon {
|
||||
// No change.
|
||||
return;
|
||||
}
|
||||
const block = this.getBlock();
|
||||
eventUtils.fire(new (eventUtils.get(eventUtils.BUBBLE_OPEN))(
|
||||
this.block_, visible, 'mutator'));
|
||||
block, visible, 'mutator'));
|
||||
if (visible) {
|
||||
// Create the bubble.
|
||||
this.bubble_ = new Bubble(
|
||||
(this.block_.workspace as WorkspaceSvg), this.createEditor_(),
|
||||
this.block_.pathObject.svgPath, (this.iconXY_ as Coordinate), null,
|
||||
null);
|
||||
block.workspace, this.createEditor_(), block.pathObject.svgPath,
|
||||
(this.iconXY_ as Coordinate), null, null);
|
||||
// Expose this mutator's block's ID on its top-level SVG group.
|
||||
this.bubble_.setSvgId(this.block_.id);
|
||||
this.bubble_.setSvgId(block.id);
|
||||
this.bubble_.registerMoveEvent(this.onBubbleMove_.bind(this));
|
||||
const tree = this.workspace_!.options.languageTree;
|
||||
const flyout = this.workspace_!.getFlyout();
|
||||
@@ -318,7 +326,7 @@ export class Mutator extends Icon {
|
||||
flyout!.show(tree);
|
||||
}
|
||||
|
||||
this.rootBlock_ = this.block_!.decompose!(this.workspace_!)!;
|
||||
this.rootBlock_ = block.decompose!(this.workspace_!)!;
|
||||
const blocks = this.rootBlock_!.getDescendants(false);
|
||||
for (let i = 0, child; child = blocks[i]; i++) {
|
||||
child.render();
|
||||
@@ -335,20 +343,21 @@ export class Mutator extends Icon {
|
||||
margin = 16;
|
||||
x = margin;
|
||||
}
|
||||
if (this.block_.RTL) {
|
||||
if (block.RTL) {
|
||||
x = -x;
|
||||
}
|
||||
this.rootBlock_!.moveBy(x, margin);
|
||||
// Save the initial connections, then listen for further changes.
|
||||
if (this.block_.saveConnections) {
|
||||
if (block.saveConnections) {
|
||||
const thisRootBlock = this.rootBlock_;
|
||||
this.block_.saveConnections(thisRootBlock);
|
||||
block.saveConnections(thisRootBlock);
|
||||
this.sourceListener_ = () => {
|
||||
if (this.block_ && this.block_.saveConnections) {
|
||||
this.block_.saveConnections(thisRootBlock);
|
||||
const currentBlock = this.getBlock();
|
||||
if (currentBlock.saveConnections) {
|
||||
currentBlock.saveConnections(thisRootBlock);
|
||||
}
|
||||
};
|
||||
this.block_.workspace.addChangeListener(this.sourceListener_);
|
||||
block.workspace.addChangeListener(this.sourceListener_);
|
||||
}
|
||||
this.resizeBubble_();
|
||||
// When the mutator's workspace changes, update the source block.
|
||||
@@ -367,7 +376,7 @@ export class Mutator extends Icon {
|
||||
this.workspaceWidth_ = 0;
|
||||
this.workspaceHeight_ = 0;
|
||||
if (this.sourceListener_) {
|
||||
this.block_.workspace.removeChangeListener(this.sourceListener_);
|
||||
block.workspace.removeChangeListener(this.sourceListener_);
|
||||
this.sourceListener_ = null;
|
||||
}
|
||||
}
|
||||
@@ -438,7 +447,7 @@ export class Mutator extends Icon {
|
||||
if (!existingGroup) {
|
||||
eventUtils.setGroup(true);
|
||||
}
|
||||
const block = this.block_ as BlockSvg;
|
||||
const block = this.getBlock();
|
||||
const oldExtraState = BlockChange.getExtraBlockState_(block);
|
||||
|
||||
// Switch off rendering while the source block is rebuilt.
|
||||
@@ -482,7 +491,7 @@ export class Mutator extends Icon {
|
||||
|
||||
/** Dispose of this mutator. */
|
||||
override dispose() {
|
||||
this.block_.mutator = null;
|
||||
this.getBlock().mutator = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ export class Warning extends Icon {
|
||||
return;
|
||||
}
|
||||
eventUtils.fire(new (eventUtils.get(eventUtils.BUBBLE_OPEN))(
|
||||
this.block_, visible, 'warning'));
|
||||
this.getBlock(), visible, 'warning'));
|
||||
if (visible) {
|
||||
this.createBubble_();
|
||||
} else {
|
||||
@@ -101,7 +101,7 @@ export class Warning extends Icon {
|
||||
private createBubble_() {
|
||||
this.paragraphElement_ = Bubble.textToDom(this.getText());
|
||||
this.bubble_ = Bubble.createNonEditableBubble(
|
||||
this.paragraphElement_, this.block_, this.iconXY_ as Coordinate);
|
||||
this.paragraphElement_, this.getBlock(), this.iconXY_ as Coordinate);
|
||||
this.applyColour();
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ export class Warning extends Icon {
|
||||
|
||||
/** Dispose of this warning. */
|
||||
override dispose() {
|
||||
this.block_.warning = null;
|
||||
this.getBlock().warning = null;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user