mirror of
https://github.com/google/blockly.git
synced 2026-01-07 17:10:11 +01:00
Dynamic output shapes (#2980)
* Round and triangle dynamic output shapes.
This commit is contained in:
@@ -180,9 +180,13 @@ Blockly.blockRendering.Drawer.prototype.drawValueInput_ = function(row) {
|
||||
var input = row.getLastInput();
|
||||
this.positionExternalValueConnection_(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.width) +
|
||||
input.shape.pathDown +
|
||||
pathDown +
|
||||
Blockly.utils.svgPaths.lineOnAxis('v', row.height - input.connectionHeight);
|
||||
};
|
||||
|
||||
@@ -267,10 +271,14 @@ Blockly.blockRendering.Drawer.prototype.drawLeft_ = function() {
|
||||
if (outputConnection) {
|
||||
var tabBottom = outputConnection.connectionOffsetY +
|
||||
outputConnection.height;
|
||||
var pathUp = (typeof outputConnection.shape.pathUp == "function") ?
|
||||
outputConnection.shape.pathUp(outputConnection.height) :
|
||||
outputConnection.shape.pathUp;
|
||||
|
||||
// Draw a line up to the bottom of the tab.
|
||||
this.outlinePath_ +=
|
||||
Blockly.utils.svgPaths.lineOnAxis('V', tabBottom) +
|
||||
outputConnection.shape.pathUp;
|
||||
pathUp;
|
||||
}
|
||||
// Close off the path. This draws a vertical line up to the start of the
|
||||
// block's path, which may be either a rounded or a sharp corner.
|
||||
|
||||
@@ -71,6 +71,15 @@ Blockly.blockRendering.OutputConnection = function(connectionModel) {
|
||||
goog.inherits(Blockly.blockRendering.OutputConnection,
|
||||
Blockly.blockRendering.Connection);
|
||||
|
||||
/**
|
||||
* Whether or not the connection shape is dynamic. Dynamic shapes get their
|
||||
* height from the block.
|
||||
* @return {boolean} True if the connection shape is dynamic.
|
||||
*/
|
||||
Blockly.blockRendering.OutputConnection.prototype.isDynamic = function() {
|
||||
return this.shape.isDynamic;
|
||||
};
|
||||
|
||||
/**
|
||||
* An object containing information about the space a previous connection takes
|
||||
* up during rendering.
|
||||
|
||||
@@ -38,20 +38,23 @@ goog.require('Blockly.utils.svgPaths');
|
||||
*/
|
||||
Blockly.zelos.ConstantProvider = function() {
|
||||
Blockly.zelos.ConstantProvider.superClass_.constructor.call(this);
|
||||
|
||||
this.GRID_UNIT = 4;
|
||||
|
||||
var GRID_UNIT = 4;
|
||||
this.CORNER_RADIUS = 1 * this.GRID_UNIT;
|
||||
|
||||
this.CORNER_RADIUS = 1 * GRID_UNIT;
|
||||
this.NOTCH_WIDTH = 9 * this.GRID_UNIT;
|
||||
|
||||
this.NOTCH_WIDTH = 9 * GRID_UNIT;
|
||||
this.NOTCH_HEIGHT = 2 * this.GRID_UNIT;
|
||||
|
||||
this.NOTCH_HEIGHT = 2 * GRID_UNIT;
|
||||
this.NOTCH_OFFSET_LEFT = 3 * this.GRID_UNIT;
|
||||
|
||||
this.NOTCH_OFFSET_LEFT = 3 * GRID_UNIT;
|
||||
|
||||
this.MIN_BLOCK_HEIGHT = 12 * GRID_UNIT;
|
||||
this.MIN_BLOCK_HEIGHT = 12 * this.GRID_UNIT;
|
||||
|
||||
this.DARK_PATH_OFFSET = 0;
|
||||
|
||||
this.TAB_OFFSET_FROM_TOP = 0;
|
||||
|
||||
};
|
||||
goog.inherits(Blockly.zelos.ConstantProvider,
|
||||
Blockly.blockRendering.ConstantProvider);
|
||||
@@ -61,35 +64,78 @@ goog.inherits(Blockly.zelos.ConstantProvider,
|
||||
*/
|
||||
Blockly.zelos.ConstantProvider.prototype.init = function() {
|
||||
Blockly.zelos.ConstantProvider.superClass_.init.call(this);
|
||||
this.TRIANGLE = this.makeTriangle();
|
||||
this.HEXAGONAL = this.makeHexagonal();
|
||||
this.ROUNDED = this.makeRounded();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {!Object} An object containing sizing and path information about
|
||||
* a triangle shape for connections.
|
||||
* a hexagonal shape for connections.
|
||||
* @package
|
||||
*/
|
||||
Blockly.zelos.ConstantProvider.prototype.makeTriangle = function() {
|
||||
var width = 20;
|
||||
var height = 20;
|
||||
Blockly.zelos.ConstantProvider.prototype.makeHexagonal = function() {
|
||||
// The 'up' and 'down' versions of the paths are the same, but the Y sign
|
||||
// flips. Forward and back are the signs to use to move the cursor in the
|
||||
// direction that the path is being drawn.
|
||||
function makeMainPath(up) {
|
||||
function makeMainPath(height, up, right) {
|
||||
var width = height / 2;
|
||||
var forward = up ? -1 : 1;
|
||||
var direction = right ? -1 : 1;
|
||||
|
||||
return Blockly.utils.svgPaths.lineTo(-width, forward * height / 2) +
|
||||
Blockly.utils.svgPaths.lineTo(width, forward * height / 2);
|
||||
return Blockly.utils.svgPaths.lineTo(-1 * direction * width, forward * height / 2) +
|
||||
Blockly.utils.svgPaths.lineTo(direction * width, forward * height / 2);
|
||||
}
|
||||
|
||||
var pathUp = makeMainPath(true);
|
||||
var pathDown = makeMainPath(false);
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
isDynamic: true,
|
||||
pathDown: function(height) {
|
||||
return makeMainPath(height, false, false);
|
||||
},
|
||||
pathUp: function(height) {
|
||||
return makeMainPath(height, true, false);
|
||||
},
|
||||
pathRightDown: function(height) {
|
||||
return makeMainPath(height, false, true);
|
||||
},
|
||||
pathRightUp: function(height) {
|
||||
return makeMainPath(height, false, true);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {!Object} An object containing sizing and path information about
|
||||
* a rounded shape for connections.
|
||||
* @package
|
||||
*/
|
||||
Blockly.zelos.ConstantProvider.prototype.makeRounded = function() {
|
||||
// The 'up' and 'down' versions of the paths are the same, but the Y sign
|
||||
// flips. Forward and back are the signs to use to move the cursor in the
|
||||
// direction that the path is being drawn.
|
||||
function makeMainPath(height, up, right) {
|
||||
var edgeWidth = height / 2;
|
||||
return Blockly.utils.svgPaths.arc('a', '0 0 ' + (up || right ? 1 : 0), edgeWidth,
|
||||
Blockly.utils.svgPaths.point(0, (up ? -1 : 1) * edgeWidth * 2));
|
||||
}
|
||||
|
||||
return {
|
||||
width: width,
|
||||
height: height,
|
||||
pathDown: pathDown,
|
||||
pathUp: pathUp
|
||||
width: 0,
|
||||
height: 0,
|
||||
isDynamic: true,
|
||||
pathDown: function(height) {
|
||||
return makeMainPath(height, false, false);
|
||||
},
|
||||
pathUp: function(height) {
|
||||
return makeMainPath(height, true, false);
|
||||
},
|
||||
pathRightDown: function(height) {
|
||||
return makeMainPath(height, false, true);
|
||||
},
|
||||
pathRightUp: function(height) {
|
||||
return makeMainPath(height, false, true);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -104,7 +150,13 @@ Blockly.zelos.ConstantProvider.prototype.shapeFor = function(
|
||||
case Blockly.OUTPUT_VALUE:
|
||||
// Includes doesn't work in IE.
|
||||
if (checks && checks.indexOf('Boolean') != -1) {
|
||||
return this.TRIANGLE;
|
||||
return this.HEXAGONAL;
|
||||
}
|
||||
if (checks && checks.indexOf('Number') != -1) {
|
||||
return this.ROUNDED;
|
||||
}
|
||||
if (checks && checks.indexOf('String') != -1) {
|
||||
return this.ROUNDED;
|
||||
}
|
||||
return this.PUZZLE_TAB;
|
||||
case Blockly.PREVIOUS_STATEMENT:
|
||||
|
||||
@@ -47,6 +47,21 @@ Blockly.zelos.Drawer = function(block, info) {
|
||||
goog.inherits(Blockly.zelos.Drawer, Blockly.blockRendering.Drawer);
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.Drawer.prototype.drawOutline_ = function() {
|
||||
if (this.info_.outputConnection &&
|
||||
this.info_.outputConnection.isDynamic()) {
|
||||
this.drawFlatTop_();
|
||||
this.drawRightDynamicConnection_();
|
||||
this.drawFlatBottom_();
|
||||
this.drawLeftDynamicConnection_();
|
||||
} else {
|
||||
Blockly.zelos.Drawer.superClass_.drawOutline_.call(this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add steps for the top corner of the block, taking into account
|
||||
* details such as hats and rounded corners.
|
||||
@@ -89,24 +104,27 @@ Blockly.zelos.Drawer.prototype.drawBottom_ = function() {
|
||||
var elems = bottomRow.elements;
|
||||
this.positionNextConnection_();
|
||||
|
||||
this.outlinePath_ +=
|
||||
Blockly.utils.svgPaths.lineOnAxis('v',
|
||||
bottomRow.height - bottomRow.descenderHeight -
|
||||
this.constants_.INSIDE_CORNERS.rightHeight);
|
||||
|
||||
var rightCornerYOffset = 0;
|
||||
var outlinePath = '';
|
||||
for (var i = elems.length - 1, elem; (elem = elems[i]); i--) {
|
||||
if (Blockly.blockRendering.Types.isNextConnection(elem)) {
|
||||
this.outlinePath_ += elem.shape.pathRight;
|
||||
outlinePath += elem.shape.pathRight;
|
||||
} else if (Blockly.blockRendering.Types.isLeftSquareCorner(elem)) {
|
||||
this.outlinePath_ += Blockly.utils.svgPaths.lineOnAxis('H', bottomRow.xPos);
|
||||
outlinePath += Blockly.utils.svgPaths.lineOnAxis('H', bottomRow.xPos);
|
||||
} else if (Blockly.blockRendering.Types.isLeftRoundedCorner(elem)) {
|
||||
this.outlinePath_ += this.constants_.OUTSIDE_CORNERS.bottomLeft;
|
||||
outlinePath += this.constants_.OUTSIDE_CORNERS.bottomLeft;
|
||||
} else if (Blockly.blockRendering.Types.isRightRoundedCorner(elem)) {
|
||||
this.outlinePath_ += this.constants_.OUTSIDE_CORNERS.bottomRight;
|
||||
outlinePath += this.constants_.OUTSIDE_CORNERS.bottomRight;
|
||||
rightCornerYOffset = this.constants_.INSIDE_CORNERS.rightHeight;
|
||||
} else if (Blockly.blockRendering.Types.isSpacer(elem)) {
|
||||
this.outlinePath_ += Blockly.utils.svgPaths.lineOnAxis('h', elem.width * -1);
|
||||
outlinePath += Blockly.utils.svgPaths.lineOnAxis('h', elem.width * -1);
|
||||
}
|
||||
}
|
||||
|
||||
this.outlinePath_ +=
|
||||
Blockly.utils.svgPaths.lineOnAxis('V',
|
||||
bottomRow.baseline - rightCornerYOffset);
|
||||
this.outlinePath_ += outlinePath;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -134,3 +152,63 @@ Blockly.zelos.Drawer.prototype.drawRightSideRow_ = function(row) {
|
||||
Blockly.utils.svgPaths.lineOnAxis('V', row.yPos + row.height);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add steps to draw the right side of an output with a dynamic connection.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.zelos.Drawer.prototype.drawRightDynamicConnection_ = function() {
|
||||
this.outlinePath_ += this.info_.outputConnection.shape.pathRightDown(
|
||||
this.info_.outputConnection.height);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add steps to draw the left side of an output with a dynamic connection.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.zelos.Drawer.prototype.drawLeftDynamicConnection_ = function() {
|
||||
this.positionOutputConnection_();
|
||||
|
||||
this.outlinePath_ += this.info_.outputConnection.shape.pathUp(
|
||||
this.info_.outputConnection.height);
|
||||
|
||||
// Close off the path. This draws a vertical line up to the start of the
|
||||
// block's path, which may be either a rounded or a sharp corner.
|
||||
this.outlinePath_ += 'z';
|
||||
};
|
||||
|
||||
/**
|
||||
* Add steps to draw a flat top row.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.zelos.Drawer.prototype.drawFlatTop_ = function() {
|
||||
var topRow = this.info_.topRow;
|
||||
this.positionPreviousConnection_();
|
||||
|
||||
this.outlinePath_ +=
|
||||
Blockly.utils.svgPaths.moveBy(topRow.xPos, this.info_.startY);
|
||||
|
||||
this.outlinePath_ += Blockly.utils.svgPaths.lineOnAxis('h', topRow.width);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add steps to draw a flat bottom row.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.zelos.Drawer.prototype.drawFlatBottom_ = function() {
|
||||
var bottomRow = this.info_.bottomRow;
|
||||
this.positionNextConnection_();
|
||||
|
||||
this.outlinePath_ +=
|
||||
Blockly.utils.svgPaths.lineOnAxis('V', bottomRow.baseline);
|
||||
|
||||
this.outlinePath_ += Blockly.utils.svgPaths.lineOnAxis('h', -bottomRow.width);
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.Drawer.prototype.drawInlineInput_ = function(input) {
|
||||
// Don't draw an inline input.
|
||||
this.positionInlineInputConnection_(input);
|
||||
};
|
||||
|
||||
@@ -88,6 +88,13 @@ goog.inherits(Blockly.zelos.RenderInfo, Blockly.blockRendering.RenderInfo);
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) {
|
||||
if (!prev || !next) {
|
||||
// No need for padding at the beginning or end of the row if the
|
||||
// output shape is dynamic.
|
||||
if (this.outputConnection && this.outputConnection.isDynamic()) {
|
||||
return this.constants_.NO_PADDING;
|
||||
}
|
||||
}
|
||||
if (!prev) {
|
||||
// Between an editable field and the beginning of the row.
|
||||
if (next && Blockly.blockRendering.Types.isField(next) && next.isEditable) {
|
||||
@@ -315,3 +322,48 @@ Blockly.zelos.RenderInfo.prototype.addAlignmentPadding_ = function(row,
|
||||
row.width += missingSpace;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.RenderInfo.prototype.finalize_ = function() {
|
||||
// Performance note: this could be combined with the draw pass, if the time
|
||||
// that this takes is excessive. But it shouldn't be, because it only
|
||||
// accesses and sets properties that already exist on the objects.
|
||||
var yCursor = 0;
|
||||
for (var i = 0, row; (row = this.rows[i]); i++) {
|
||||
row.yPos = yCursor;
|
||||
yCursor += row.height;
|
||||
}
|
||||
// Dynamic output connections depend on the height of the block. Adjust the
|
||||
// height and width of the connection, and then adjust the startX and width of the
|
||||
// block accordingly.
|
||||
var outputConnectionWidth = 0;
|
||||
if (this.outputConnection && !this.outputConnection.height) {
|
||||
this.outputConnection.height = yCursor;
|
||||
outputConnectionWidth = yCursor; // Twice the width to account for the right side.
|
||||
this.outputConnection.width = outputConnectionWidth / 2;
|
||||
}
|
||||
this.startX += outputConnectionWidth / 2;
|
||||
this.width += outputConnectionWidth;
|
||||
|
||||
var widestRowWithConnectedBlocks = 0;
|
||||
for (var i = 0, row; (row = this.rows[i]); i++) {
|
||||
row.xPos = this.startX;
|
||||
|
||||
widestRowWithConnectedBlocks =
|
||||
Math.max(widestRowWithConnectedBlocks, row.widthWithConnectedBlocks);
|
||||
var xCursor = row.xPos;
|
||||
for (var j = 0, elem; (elem = row.elements[j]); j++) {
|
||||
elem.xPos = xCursor;
|
||||
elem.centerline = this.getElemCenterline_(row, elem);
|
||||
xCursor += elem.width;
|
||||
}
|
||||
}
|
||||
|
||||
this.widthWithChildren = widestRowWithConnectedBlocks + this.startX;
|
||||
|
||||
this.height = yCursor;
|
||||
this.startY = this.topRow.capline;
|
||||
this.bottomRow.baseline = yCursor - this.bottomRow.descenderHeight;
|
||||
};
|
||||
|
||||
@@ -70,21 +70,21 @@ Blockly.zelos.TopRow.prototype.populate = function(block) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Never render a left square corner. Always round.
|
||||
* Render a round corner unless the block has an output connection.
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.TopRow.prototype.hasLeftSquareCorner = function(_block) {
|
||||
return false;
|
||||
Blockly.zelos.TopRow.prototype.hasLeftSquareCorner = function(block) {
|
||||
return !!block.outputConnection;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether or not the top row has a right square corner.
|
||||
* @param {!Blockly.BlockSvg} _block The block whose top row this represents.
|
||||
* @param {!Blockly.BlockSvg} block The block whose top row this represents.
|
||||
* @returns {boolean} Whether or not the top row has a left square corner.
|
||||
*/
|
||||
Blockly.zelos.TopRow.prototype.hasRightSquareCorner = function(_block) {
|
||||
// Never render a right square corner. Always round.
|
||||
return false;
|
||||
Blockly.zelos.TopRow.prototype.hasRightSquareCorner = function(block) {
|
||||
// Render a round corner unless the block has an output connection.
|
||||
return !!block.outputConnection;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -120,21 +120,21 @@ Blockly.zelos.BottomRow.prototype.populate = function(block) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Never render a left square corner. Always round.
|
||||
* Render a round corner unless the block has an output connection.
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.BottomRow.prototype.hasLeftSquareCorner = function(_block) {
|
||||
return false;
|
||||
Blockly.zelos.BottomRow.prototype.hasLeftSquareCorner = function(block) {
|
||||
return !!block.outputConnection;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether or not the bottom row has a right square corner.
|
||||
* @param {!Blockly.BlockSvg} _block The block whose bottom row this represents.
|
||||
* @param {!Blockly.BlockSvg} block The block whose bottom row this represents.
|
||||
* @returns {boolean} Whether or not the bottom row has a left square corner.
|
||||
*/
|
||||
Blockly.zelos.BottomRow.prototype.hasRightSquareCorner = function(_block) {
|
||||
// Never render a right square corner. Always round.
|
||||
return false;
|
||||
Blockly.zelos.BottomRow.prototype.hasRightSquareCorner = function(block) {
|
||||
// Render a round corner unless the block has an output connection.
|
||||
return !!block.outputConnection;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,6 +91,7 @@ function start() {
|
||||
match = location.search.match(/side=([^&]+)/);
|
||||
var side = match ? match[1] : 'start';
|
||||
document.forms.options.elements.side.value = side;
|
||||
var autoimport = !!location.search.match(/autoimport=([^&]+)/);
|
||||
// Create main workspace.
|
||||
workspace = Blockly.inject('blocklyDiv',
|
||||
{
|
||||
@@ -146,6 +147,9 @@ function start() {
|
||||
logEvents(false);
|
||||
}
|
||||
taChange();
|
||||
if (autoimport) {
|
||||
fromXml();
|
||||
}
|
||||
}
|
||||
|
||||
function addToolboxButtonCallbacks() {
|
||||
|
||||
Reference in New Issue
Block a user