Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement AI indented value inputs for Blockly #3219

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions blocks/procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,58 @@ Blockly.Blocks['procedures_defreturn'] = {
callType_: 'procedures_callreturn'
};

Blockly.Blocks['procedures_defreturnexpr'] = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to add this as a built-in block. We could add it under test blocks instead?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good point. Yes, test blocks is a better home.

init: function() {
var nameField = new Blockly.FieldTextInput('', Blockly.Procedures.rename);
nameField.setSpellcheck(false);
this.appendDummyInput()
.appendField(Blockly.Msg['PROCEDURES_DEFRETURN_TITLE'])
.appendField(nameField, 'NAME')
.appendField('', 'PARAMS');
this.appendValueInput('RETURN', {style: Blockly.INDENTED_VALUE})
.setAlign(Blockly.ALIGN_RIGHT)
.appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']);
this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
if ((this.workspace.options.comments ||
(this.workspace.options.parentWorkspace &&
this.workspace.options.parentWorkspace.options.comments)) &&
Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']) {
this.setCommentText(Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']);
}
this.setStyle('procedure_blocks');
this.setTooltip(Blockly.Msg['PROCEDURES_DEFRETURN_TOOLTIP']);
this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFRETURN_HELPURL']);
this.arguments_ = [];
this.argumentVarModels_ = [];
this.setStatements_(false);
this.statementConnection_ = null;
},
setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_,
updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_,
mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom,
domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation,
decompose: Blockly.Blocks['procedures_defnoreturn'].decompose,
compose: Blockly.Blocks['procedures_defnoreturn'].compose,
/**
* Return the signature of this procedure definition.
* @return {!Array} Tuple containing three elements:
* - the name of the defined procedure,
* - a list of all its arguments,
* - that it DOES have a return value.
* @this {Blockly.Block}
*/
getProcedureDef: function() {
return [this.getFieldValue('NAME'), this.arguments_, true];
},
getVars: Blockly.Blocks['procedures_defnoreturn'].getVars,
getVarModels: Blockly.Blocks['procedures_defnoreturn'].getVarModels,
renameVarById: Blockly.Blocks['procedures_defnoreturn'].renameVarById,
updateVarName: Blockly.Blocks['procedures_defnoreturn'].updateVarName,
displayRenamedVar_: Blockly.Blocks['procedures_defnoreturn'].displayRenamedVar_,
customContextMenu: Blockly.Blocks['procedures_defnoreturn'].customContextMenu,
callType_: 'procedures_callreturn'
};

Blockly.Blocks['procedures_mutatorcontainer'] = {
/**
* Mutator block for procedure container.
Expand Down
12 changes: 8 additions & 4 deletions core/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -1385,10 +1385,12 @@ Blockly.Block.prototype.toString = function(opt_maxLength, opt_emptyToken) {
* Shortcut for appending a value input row.
* @param {string} name Language-neutral identifier which may used to find this
* input again. Should be unique to this block.
* @param {?Object.<string, *>=} opt_extras Extra information about the input.
* - style: Rendering style hint (undefined or {@see Blockly.INDENTED_VALUE})
* @return {!Blockly.Input} The input object created.
*/
Blockly.Block.prototype.appendValueInput = function(name) {
return this.appendInput_(Blockly.INPUT_VALUE, name);
Blockly.Block.prototype.appendValueInput = function(name, opt_extras) {
return this.appendInput_(Blockly.INPUT_VALUE, name, opt_extras);
};

/**
Expand Down Expand Up @@ -1681,15 +1683,17 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) {
* Blockly.DUMMY_INPUT.
* @param {string} name Language-neutral identifier which may used to find this
* input again. Should be unique to this block.
* @param {?Object.<string, *>=} opt_extras Extra information about the input.
* - style: Rendering style hint (undefined or {@see Blockly.INDENTED_VALUE})
* @return {!Blockly.Input} The input object created.
* @protected
*/
Blockly.Block.prototype.appendInput_ = function(type, name) {
Blockly.Block.prototype.appendInput_ = function(type, name, opt_extras) {
var connection = null;
if (type == Blockly.INPUT_VALUE || type == Blockly.NEXT_STATEMENT) {
connection = this.makeConnection_(type);
}
var input = new Blockly.Input(type, name, this, connection);
var input = new Blockly.Input(type, name, this, connection, opt_extras);
// Append input to list.
this.inputList.push(input);
return input;
Expand Down
7 changes: 5 additions & 2 deletions core/block_svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -1487,11 +1487,14 @@ Blockly.BlockSvg.prototype.moveNumberedInputBefore = function(
* Blockly.DUMMY_INPUT.
* @param {string} name Language-neutral identifier which may used to find this
* input again. Should be unique to this block.
* @param {?Object.<string, *>=} opt_extras Extra information describing the
* input.
* @return {!Blockly.Input} The input object created.
* @private
*/
Blockly.BlockSvg.prototype.appendInput_ = function(type, name) {
var input = Blockly.BlockSvg.superClass_.appendInput_.call(this, type, name);
Blockly.BlockSvg.prototype.appendInput_ = function(type, name, opt_extras) {
var input = Blockly.BlockSvg.superClass_.appendInput_.call(this, type, name,
opt_extras);

if (this.rendered) {
this.render();
Expand Down
7 changes: 7 additions & 0 deletions core/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ Blockly.ALIGN_CENTRE = 0;
*/
Blockly.ALIGN_RIGHT = 1;

/***
* ENUM to indicate that a value input should be indented like
* a statement input.
* @const
*/
Blockly.INDENTED_VALUE = 1;

/**
* ENUM for no drag operation.
* @const
Expand Down
6 changes: 5 additions & 1 deletion core/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ goog.require('Blockly.FieldLabel');
* input again.
* @param {!Blockly.Block} block The block containing this input.
* @param {Blockly.Connection} connection Optional connection for this input.
* @param {?Object.<string, *>=} opt_extras Extra information about the input.
* - style: Rendering style hint (undefined or {@see Blockly.INDENTED_VALUE})
* @constructor
*/
Blockly.Input = function(type, name, block, connection) {
Blockly.Input = function(type, name, block, connection, opt_extras) {
if (type != Blockly.DUMMY_INPUT && !name) {
throw Error('Value inputs and statement inputs must have non-empty name.');
}
Expand All @@ -53,6 +55,8 @@ Blockly.Input = function(type, name, block, connection) {
this.connection = connection;
/** @type {!Array.<!Blockly.Field>} */
this.fieldRow = [];
/** @type {!Object.<string, *>} */
this.extraInfo = opt_extras || {};
};

/**
Expand Down
14 changes: 14 additions & 0 deletions core/procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,20 @@ Blockly.Procedures.flyoutCategory = function(workspace) {
block.appendChild(nameField);
xmlList.push(block);
}
if (Blockly.Blocks['procedures_defreturnexpr']) {
// <block type="procedures_defreturnexpr" gap="16">
// <field name="NAME">do something</field>
// </block>
var block = Blockly.utils.xml.createElement('block');
block.setAttribute('type', 'procedures_defreturnexpr');
block.setAttribute('gap', 16);
var nameField = Blockly.utils.xml.createElement('field');
nameField.setAttribute('name', 'NAME');
nameField.appendChild(Blockly.utils.xml.createTextNode(
Blockly.Msg['PROCEDURES_DEFRETURN_PROCEDURE']));
block.appendChild(nameField);
xmlList.push(block);
}
if (Blockly.Blocks['procedures_ifreturn']) {
// <block type="procedures_ifreturn" gap="16"></block>
var block = Blockly.utils.xml.createElement('block');
Expand Down
44 changes: 44 additions & 0 deletions core/renderers/common/drawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ Blockly.blockRendering.Drawer.prototype.drawOutline_ = function() {
this.drawJaggedEdge_(row);
} else if (row.hasStatement) {
this.drawStatementInput_(row);
} else if (row.hasIndentedInput) {
this.drawIndentedInput_(row);
} else if (row.hasExternalInput) {
this.drawValueInput_(row);
} else {
Expand Down Expand Up @@ -190,6 +192,30 @@ Blockly.blockRendering.Drawer.prototype.drawValueInput_ = function(row) {
};


/**
* Add steps for an indented value input, rendered as a notch in the side
* of the block but at the same X value as any statement inputs.
* @param {!Blockly.blockRendering.Row} row The row that this input
* belongs to.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.drawIndentedInput_ = function(row) {
var input = row.getLastInput();
this.positionIndentedValueConnection_(row);

var pathDown = (typeof input.shape.pathDown == "function") ?
input.shape.pathDown(input.height) :
input.shape.pathDown;

this.outlinePath_ +=
Blockly.utils.svgPaths.lineOnAxis('H', input.xPos + input.connectionWidth) +
Blockly.utils.svgPaths.lineOnAxis('v', input.connectionOffsetY) +
pathDown +
Blockly.utils.svgPaths.lineOnAxis('v', row.height - input.connectionHeight - input.connectionOffsetY) +
Blockly.utils.svgPaths.lineOnAxis('H', input.xPos + input.width);
};


/**
* Add steps for a statement input.
* @param {!Blockly.blockRendering.Row} row The row that this input
Expand Down Expand Up @@ -427,6 +453,24 @@ Blockly.blockRendering.Drawer.prototype.positionExternalValueConnection_ = funct
}
};

/**
* Position the connection on an indented value input, taking into account
* RTL and the small gap between the parent block and child block which lets the
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove the comment on the small gap, since that doesn't apply for the base drawer.

* parent block's dark path show through.
* @param {!Blockly.blockRendering.Row} row The row that the connection is on.
* @protected
*/
Blockly.blockRendering.Drawer.prototype.positionIndentedValueConnection_ = function(row) {
var input = row.getLastInput();
if (input.connection) {
var connX = row.xPos + row.statementEdge + input.connectionWidth;
if (this.info_.RTL) {
connX *= -1;
}
input.connection.setOffsetInBlock(connX, row.yPos + input.connectionOffsetY);
}
};

/**
* Position the previous connection on a block.
* @protected
Expand Down
11 changes: 8 additions & 3 deletions core/renderers/common/info.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,11 @@ Blockly.blockRendering.RenderInfo.prototype.addInput_ = function(input, activeRo
activeRow.elements.push(
new Blockly.blockRendering.StatementInput(this.constants_, input));
activeRow.hasStatement = true;
} else if (input.type == Blockly.INPUT_VALUE &&
input.extraInfo.style == Blockly.INDENTED_VALUE) {
activeRow.elements.push(
new Blockly.blockRendering.IndentedValueInput(this.constants_, input));
activeRow.hasIndentedInput = true;
} else if (input.type == Blockly.INPUT_VALUE) {
activeRow.elements.push(
new Blockly.blockRendering.ExternalValueInput(this.constants_, input));
Expand Down Expand Up @@ -466,7 +471,7 @@ Blockly.blockRendering.RenderInfo.prototype.computeBounds_ = function() {
for (var i = 0, row; (row = this.rows[i]); i++) {
row.measure();
blockWidth = Math.max(blockWidth, row.width);
if (row.hasStatement) {
if (row.isStatementLike()) {
var statementInput = row.getLastInput();
var innerWidth = row.width - statementInput.width;
widestStatementRowFields = Math.max(widestStatementRowFields, innerWidth);
Expand All @@ -479,7 +484,7 @@ Blockly.blockRendering.RenderInfo.prototype.computeBounds_ = function() {
this.width = blockWidth;

for (var i = 0, row; (row = this.rows[i]); i++) {
if (row.hasStatement) {
if (row.isStatementLike()) {
row.statementEdge = this.statementEdge;
}
}
Expand All @@ -501,7 +506,7 @@ Blockly.blockRendering.RenderInfo.prototype.computeBounds_ = function() {
*/
Blockly.blockRendering.RenderInfo.prototype.alignRowElements_ = function() {
for (var i = 0, row; (row = this.rows[i]); i++) {
if (row.hasStatement) {
if (row.isStatementLike()) {
this.alignStatementRow_(
/** @type {!Blockly.blockRendering.InputRow} */ (row));
} else {
Expand Down
9 changes: 9 additions & 0 deletions core/renderers/geras/drawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ Blockly.geras.Drawer.prototype.drawValueInput_ = function(row) {
Blockly.geras.Drawer.superClass_.drawValueInput_.call(this, row);
};

/**
* @override
*/
Blockly.geras.Drawer.prototype.drawIndentedInput_ = function(row) {
this.highlighter_.drawIndentedInput(row);

Blockly.geras.Drawer.superClass_.drawIndentedInput_.call(this, row);
};

/**
* @override
*/
Expand Down
19 changes: 19 additions & 0 deletions core/renderers/geras/highlighter.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,25 @@ Blockly.geras.Highlighter.prototype.drawValueInput = function(row) {
}
};

Blockly.geras.Highlighter.prototype.drawIndentedInput = function(row) {
var input = row.getLastInput();
if (this.RTL_) {
var belowTabHeight = row.height - input.connectionHeight - input.connectionOffsetY;

this.steps_ +=
Blockly.utils.svgPaths.moveTo(
input.xPos + this.highlightOffset_ + input.connectionWidth - 1, row.yPos) +
Copy link
Collaborator

Choose a reason for hiding this comment

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

This -1 is probably HIGHLIGHT_OFFSET.

Blockly.utils.svgPaths.lineOnAxis('v', input.connectionOffsetY) +
this.puzzleTabPaths_.pathDown(this.RTL_) +
Blockly.utils.svgPaths.lineOnAxis('v', belowTabHeight);
} else {
this.steps_ +=
Blockly.utils.svgPaths.moveTo(input.xPos + input.connectionWidth,
row.yPos + input.connectionOffsetY) +
this.puzzleTabPaths_.pathDown(this.RTL_);
}
};

Blockly.geras.Highlighter.prototype.drawStatementInput = function(row) {
var input = row.getLastInput();
if (this.RTL_) {
Expand Down
9 changes: 7 additions & 2 deletions core/renderers/geras/info.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ Blockly.geras.RenderInfo.prototype.addInput_ = function(input, activeRow) {
activeRow.elements.push(
new Blockly.geras.StatementInput(this.constants_, input));
activeRow.hasStatement = true;
} else if (input.type == Blockly.INPUT_VALUE &&
input.extraInfo.style == Blockly.INDENTED_VALUE) {
activeRow.elements.push(
new Blockly.blockRendering.IndentedValueInput(this.constants_, input));
activeRow.hasIndentedInput = true;
} else if (input.type == Blockly.INPUT_VALUE) {
activeRow.elements.push(
new Blockly.blockRendering.ExternalValueInput(this.constants_, input));
Expand Down Expand Up @@ -248,7 +253,7 @@ Blockly.geras.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) {
Blockly.geras.RenderInfo.prototype.addAlignmentPadding_ = function(row, missingSpace) {
var firstSpacer = row.getFirstSpacer();
var lastSpacer = row.getLastSpacer();
if (row.hasExternalInput || row.hasStatement) {
if (row.hasExternalInput || row.hasStatement || row.hasIndentedInput) {
row.widthWithConnectedBlocks += missingSpace;
}

Expand Down Expand Up @@ -327,7 +332,7 @@ Blockly.geras.RenderInfo.prototype.getElemCenterline_ = function(row, elem) {
if (Blockly.blockRendering.Types.isField(elem) ||
Blockly.blockRendering.Types.isIcon(elem)) {
result += (elem.height / 2);
if ((row.hasInlineInput || row.hasStatement) &&
if ((row.hasInlineInput || row.hasStatement || row.hasIndentedInput) &&
elem.height + this.constants_.TALL_INPUT_FIELD_OFFSET_Y <= row.height) {
result += this.constants_.TALL_INPUT_FIELD_OFFSET_Y;
}
Expand Down
21 changes: 21 additions & 0 deletions core/renderers/measurables/inputs.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,24 @@ Blockly.blockRendering.ExternalValueInput = function(constants, input) {
};
Blockly.utils.object.inherits(Blockly.blockRendering.ExternalValueInput,
Blockly.blockRendering.InputConnection);

/**
* An object containing information about the space an indented value input
* takes up during rendering
* @param {!Blockly.blockRendering.ConstantProvider} constants The rendering
* constants provider.
* @param {!Blockly.Input} input The indented value input to measure and store
* information for.
* @constructor
Copy link
Contributor

Choose a reason for hiding this comment

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

Add @extends {Blockly.blockRendering.ExternalValueInput}

*/
Blockly.blockRendering.IndentedValueInput = function(constants, input) {
Blockly.blockRendering.IndentedValueInput.superClass_.constructor.call(this,
constants, input);
if (!this.connectedBlock) {
this.height = this.constants_.EMPTY_STATEMENT_INPUT_HEIGHT;
} else {
this.height = this.connectedBlockHeight;
}
};
Blockly.utils.object.inherits(Blockly.blockRendering.IndentedValueInput,
Blockly.blockRendering.ExternalValueInput);
17 changes: 17 additions & 0 deletions core/renderers/measurables/rows.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ Blockly.blockRendering.Row = function(constants) {
*/
this.hasExternalInput = false;

/**
* Whether the row has any indented inputs.
* @package
* @type {boolean}
*/
this.hasIndentedInput = false;

/**
* Whether the row has any statement inputs.
* @package
Expand Down Expand Up @@ -156,6 +163,16 @@ Blockly.blockRendering.Row = function(constants) {
this.notchOffset = this.constants_.NOTCH_OFFSET_LEFT;
};

/**
* Returns whether the row should be treated like a statement during the
* measuring pass.
* @return {boolean} true if the row should measure like a statement input,
* otherwise false.
*/
Blockly.blockRendering.Row.prototype.isStatementLike = function() {
return this.hasStatement || this.hasIndentedInput;
};

/**
* Inspect all subcomponents and populate all size properties on the row.
* @package
Expand Down