mirror of
https://github.com/google/blockly.git
synced 2026-01-08 09:30:06 +01:00
Pull all the new rendering files into develop
This commit is contained in:
430
core/renderers/block_rendering_rewrite/block_render_draw.js
Normal file
430
core/renderers/block_rendering_rewrite/block_render_draw.js
Normal file
@@ -0,0 +1,430 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2019 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Methods for graphically rendering a block as SVG.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
|
||||
//'use strict';
|
||||
goog.provide('Blockly.BlockRendering.Drawer');
|
||||
|
||||
goog.require('Blockly.BlockRendering.Debug');
|
||||
goog.require('Blockly.BlockRendering.RenderInfo');
|
||||
goog.require('Blockly.BlockRendering.Highlighter');
|
||||
goog.require('BRC');
|
||||
/* global BRC */
|
||||
goog.require('Blockly.BlockRendering.Measurable');
|
||||
|
||||
/**
|
||||
* Render the given block.
|
||||
* @param {!Blockly.BlockSvg} block The block to render
|
||||
* @public
|
||||
*/
|
||||
Blockly.BlockRendering.render = function(block) {
|
||||
if (!block.renderingDebugger) {
|
||||
block.renderingDebugger = new Blockly.BlockRendering.Debug();
|
||||
}
|
||||
new Blockly.BlockRendering.Drawer(block).draw_();
|
||||
};
|
||||
|
||||
/**
|
||||
* An object that draws a block based on the given rendering information.
|
||||
* @param {!Blockly.BlockSvg} block The block to render
|
||||
* @param {!Blockly.BlockRendering.RenderInfo} info An object containing all
|
||||
* information needed to render this block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer = function(block) {
|
||||
this.block_ = block;
|
||||
this.topLeft_ = block.getRelativeToSurfaceXY();
|
||||
this.info_ = new Blockly.BlockRendering.RenderInfo(block);
|
||||
this.pathObject_ = new Blockly.BlockSvg.PathObject();
|
||||
this.steps_ = this.pathObject_.steps;
|
||||
this.inlineSteps_ = this.pathObject_.inlineSteps;
|
||||
this.highlighter_ =
|
||||
new Blockly.BlockRendering.Highlighter(this.info_, this.pathObject_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the block to the workspace. Here "drawing" means setting SVG path
|
||||
* elements and moving fields, icons, and connections on the screen.
|
||||
*
|
||||
* The pieces of the paths are pushed into arrays of "steps", which are then
|
||||
* joined with spaces and set directly on the block. This guarantees that
|
||||
* the steps are separated by spaces for improved readability, but isn't
|
||||
* required.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.draw_ = function() {
|
||||
this.drawOutline_();
|
||||
this.drawInternals_();
|
||||
this.block_.setPaths_(this.pathObject_);
|
||||
this.block_.renderingDebugger.drawDebug(this.block_, this.info_);
|
||||
this.recordSizeOnBlock_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Save sizing information back to the block
|
||||
* Most of the rendering information can be thrown away at the end of the render.
|
||||
* Anything that needs to be kept around should be set in this function.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.recordSizeOnBlock_ = function() {
|
||||
// This is used when the block is reporting its size to anyone else.
|
||||
// The dark path adds to the size of the block in both X and Y.
|
||||
this.block_.height = this.info_.height + BRC.DARK_PATH_OFFSET;
|
||||
this.block_.width = this.info_.widthWithChildren + BRC.DARK_PATH_OFFSET;
|
||||
// The flyout uses this information.
|
||||
this.block_.startHat_ = this.info_.topRow.startHat;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the outline of the block. This is a single continuous path.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.drawOutline_ = function() {
|
||||
this.drawTop_();
|
||||
for (var r = 1; r < this.info_.rows.length - 1; r++) {
|
||||
var row = this.info_.rows[r];
|
||||
if (row.hasStatement) {
|
||||
this.drawStatementInput_(row);
|
||||
} else if (row.hasExternalInput) {
|
||||
this.drawValueInput_(row);
|
||||
} else {
|
||||
this.drawRightSideRow_(row);
|
||||
}
|
||||
}
|
||||
this.drawBottom_();
|
||||
this.drawLeft_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add steps for the top corner of the block, taking into account
|
||||
* details such as hats and rounded corners.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.drawTop_ = function() {
|
||||
var topRow = this.info_.topRow;
|
||||
var elements = topRow.elements;
|
||||
|
||||
this.highlighter_.drawTopCorner(topRow);
|
||||
this.highlighter_.drawRightSideRow(topRow);
|
||||
this.positionPreviousConnection_();
|
||||
|
||||
for (var i = 0, elem; elem = elements[i]; i++) {
|
||||
if (elem.type === 'square corner') {
|
||||
this.steps_.push(BRC.START_POINT);
|
||||
} else if (elem.type === 'round corner') {
|
||||
this.steps_.push(BRC.TOP_LEFT_CORNER_START, BRC.TOP_LEFT_CORNER);
|
||||
} else if (elem.type === 'previous connection') {
|
||||
this.steps_.push(BRC.NOTCH_PATH_LEFT);
|
||||
} else if (elem.type === 'hat') {
|
||||
this.steps_.push(BRC.START_HAT_PATH);
|
||||
} else if (elem.isSpacer()) {
|
||||
this.steps_.push('h', elem.width);
|
||||
}
|
||||
}
|
||||
this.steps_.push('v', topRow.height);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add steps for an external value input, rendered as a notch in the side
|
||||
* of the block.
|
||||
* @param {!Blockly.BlockRendering.Row} row The row that this input
|
||||
* belongs to.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.drawValueInput_ = function(row) {
|
||||
this.highlighter_.drawValueInput(row);
|
||||
this.steps_.push('H', row.width);
|
||||
this.steps_.push(BRC.TAB_PATH_DOWN);
|
||||
this.steps_.push('v', row.height - BRC.TAB_HEIGHT);
|
||||
this.positionExternalValueConnection_(row);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add steps for a statement input.
|
||||
* @param {!Blockly.BlockRendering.Row} row The row that this input
|
||||
* belongs to.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.drawStatementInput_ = function(row) {
|
||||
this.highlighter_.drawStatementInput(row);
|
||||
var x = row.statementEdge + BRC.NOTCH_OFFSET_RIGHT;
|
||||
this.steps_.push('H', x);
|
||||
this.steps_.push(BRC.INNER_TOP_LEFT_CORNER);
|
||||
this.steps_.push('v', row.height - 2 * BRC.CORNER_RADIUS);
|
||||
this.steps_.push(BRC.INNER_BOTTOM_LEFT_CORNER);
|
||||
|
||||
this.positionStatementInputConnection_(row);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add steps for the right side of a row that does not have value or
|
||||
* statement input connections.
|
||||
* @param {!Blockly.BlockRendering.Row} row The row to draw the
|
||||
* side of.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.drawRightSideRow_ = function(row) {
|
||||
this.highlighter_.drawRightSideRow(row);
|
||||
this.steps_.push('H', row.width);
|
||||
this.steps_.push('v', row.height);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add steps for the bottom edge of a block, possibly including a notch
|
||||
* for the next connection
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.drawBottom_ = function() {
|
||||
var bottomRow = this.info_.bottomRow;
|
||||
var elems = bottomRow.elements;
|
||||
this.highlighter_.drawBottomCorner(bottomRow);
|
||||
this.positionNextConnection_();
|
||||
this.steps_.push('v', bottomRow.height);
|
||||
for (var i = elems.length - 1; i >= 0; i--) {
|
||||
var elem = elems[i];
|
||||
if (elem.type === 'next connection') {
|
||||
this.steps_.push(BRC.NOTCH_PATH_RIGHT);
|
||||
} else if (elem.type === 'square corner') {
|
||||
this.steps_.push('H 0');
|
||||
} else if (elem.type === 'round corner') {
|
||||
this.steps_.push(BRC.BOTTOM_LEFT_CORNER);
|
||||
} else if (elem.isSpacer()) {
|
||||
this.steps_.push('h', elem.width * -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add steps for the left side of the block, which may include an output
|
||||
* connection
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.drawLeft_ = function() {
|
||||
this.highlighter_.drawLeft();
|
||||
|
||||
this.positionOutputConnection_();
|
||||
if (this.info_.hasOutputConnection) {
|
||||
// Draw a line up to the bottom of the tab.
|
||||
this.steps_.push('V', BRC.TAB_OFFSET_FROM_TOP + BRC.TAB_HEIGHT);
|
||||
this.steps_.push(BRC.TAB_PATH_UP);
|
||||
}
|
||||
// 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.steps_.push('z');
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the internals of the block: inline inputs, fields, and icons. These do
|
||||
* not depend on the outer path for placement.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.drawInternals_ = function() {
|
||||
for (var r = 0; r < this.info_.rows.length; r++) {
|
||||
var row = this.info_.rows[r];
|
||||
if (!(row.isSpacer())) {
|
||||
for (var e = 0; e < row.elements.length; e++) {
|
||||
var elem = row.elements[e];
|
||||
if (elem.isInlineInput()) {
|
||||
this.drawInlineInput_(elem);
|
||||
} else if (elem.isIcon() || elem.isField()) {
|
||||
this.layoutField_(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Some fields are terrible and render offset from where they claim to be
|
||||
* rendered. This function calculates an x offset for fields that need it.
|
||||
* No one is happy about this.
|
||||
* @param {!Blockly.Field} field The field to find an offset for.
|
||||
* @return {number} How far to offset the field in the x direction.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.dealWithJackassFields_ = function(field) {
|
||||
if (field instanceof Blockly.FieldDropdown
|
||||
|| field instanceof Blockly.FieldTextInput
|
||||
|| field instanceof Blockly.FieldColour
|
||||
|| field instanceof Blockly.FieldCheckbox) {
|
||||
return 5;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Push a field or icon's new position to its SVG root.
|
||||
* @param {!Blockly.BlockRendering.Icon|!Blockly.BlockRendering.Field} fieldInfo
|
||||
* The rendering information for the field or icon.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.layoutField_ = function(fieldInfo) {
|
||||
if (fieldInfo.type == 'field') {
|
||||
var svgGroup = fieldInfo.field.getSvgRoot();
|
||||
} else if (fieldInfo.type == 'icon') {
|
||||
var svgGroup = fieldInfo.icon.iconGroup_;
|
||||
}
|
||||
|
||||
var yPos = fieldInfo.centerline - fieldInfo.height / 2;
|
||||
var xPos = fieldInfo.xPos;
|
||||
if (this.info_.RTL) {
|
||||
xPos = -(xPos + fieldInfo.width);
|
||||
}
|
||||
if (fieldInfo.type == 'icon') {
|
||||
svgGroup.setAttribute('display', 'block');
|
||||
svgGroup.setAttribute('transform', 'translate(' + xPos + ',' + yPos + ')');
|
||||
fieldInfo.icon.computeIconLocation();
|
||||
} else {
|
||||
xPos += this.dealWithJackassFields_(fieldInfo.field);
|
||||
|
||||
svgGroup.setAttribute('transform', 'translate(' + xPos + ',' + yPos + ')');
|
||||
}
|
||||
|
||||
if (this.info_.isInsertionMarker) {
|
||||
// Fields and icons are invisible on insertion marker. They still have to
|
||||
// be rendered so that the block can be sized correctly.
|
||||
svgGroup.setAttribute('display', 'none');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add steps for an inline input.
|
||||
* @param {Blockly.BlockRendering.RenderableInput} input The information about the
|
||||
* input to render.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.drawInlineInput_ = function(input) {
|
||||
this.highlighter_.drawInlineInput(input);
|
||||
var width = input.width;
|
||||
var height = input.height;
|
||||
var yPos = input.centerline - height / 2;
|
||||
|
||||
this.inlineSteps_.push('M', (input.xPos + BRC.TAB_WIDTH) + ',' + yPos);
|
||||
this.inlineSteps_.push('v ', BRC.TAB_OFFSET_FROM_TOP);
|
||||
this.inlineSteps_.push(BRC.TAB_PATH_DOWN);
|
||||
this.inlineSteps_.push('v', height - BRC.TAB_HEIGHT - BRC.TAB_OFFSET_FROM_TOP);
|
||||
this.inlineSteps_.push('h', width - BRC.TAB_WIDTH);
|
||||
this.inlineSteps_.push('v', -height);
|
||||
this.inlineSteps_.push('z');
|
||||
|
||||
this.positionInlineInputConnection_(input);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Position the connection on an inline value input, taking into account
|
||||
* RTL and the small gap between the parent block and child block which lets the
|
||||
* parent block's dark path show through.
|
||||
* @param {Blockly.BlockRendering.RenderableInput} input The information about
|
||||
* the input that the connection is on.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.positionInlineInputConnection_ = function(input) {
|
||||
var yPos = input.centerline - input.height / 2;
|
||||
// Move the connection.
|
||||
if (input.connection) {
|
||||
var connX = input.xPos + BRC.TAB_WIDTH + BRC.DARK_PATH_OFFSET;
|
||||
if (this.info_.RTL) {
|
||||
connX *= -1;
|
||||
}
|
||||
input.connection.setOffsetInBlock(
|
||||
connX, yPos + BRC.TAB_OFFSET_FROM_TOP + BRC.DARK_PATH_OFFSET);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Position the connection on a statement input, taking into account
|
||||
* RTL and the small gap between the parent block and child block which lets the
|
||||
* parent block's dark path show through.
|
||||
* @param {!Blockly.BlockRendering.Row} row The row that the connection is on.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.positionStatementInputConnection_ = function(row) {
|
||||
var input = row.getLastInput();
|
||||
if (input.connection) {
|
||||
var connX = row.statementEdge + BRC.NOTCH_OFFSET_LEFT + BRC.DARK_PATH_OFFSET;
|
||||
if (this.info_.RTL) {
|
||||
connX *= -1;
|
||||
}
|
||||
input.connection.setOffsetInBlock(connX, row.yPos + BRC.DARK_PATH_OFFSET);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Position the connection on an external value input, taking into account
|
||||
* RTL and the small gap between the parent block and child block which lets the
|
||||
* parent block's dark path show through.
|
||||
* @param {!Blockly.BlockRendering.Row} row The row that the connection is on.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.positionExternalValueConnection_ = function(row) {
|
||||
var input = row.getLastInput();
|
||||
if (input.connection) {
|
||||
var connX = row.width + BRC.DARK_PATH_OFFSET;
|
||||
if (this.info_.RTL) {
|
||||
connX *= -1;
|
||||
}
|
||||
input.connection.setOffsetInBlock(connX, row.yPos);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Position the previous connection on a block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.positionPreviousConnection_ = function() {
|
||||
if (this.info_.topRow.hasPreviousConnection) {
|
||||
var connX = this.info_.RTL ? -BRC.NOTCH_OFFSET_LEFT : BRC.NOTCH_OFFSET_LEFT;
|
||||
this.info_.topRow.connection.setOffsetInBlock(connX, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Position the next connection on a block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.positionNextConnection_ = function() {
|
||||
var bottomRow = this.info_.bottomRow;
|
||||
|
||||
if (bottomRow.hasNextConnection) {
|
||||
var connX = this.info_.RTL ? -BRC.NOTCH_OFFSET_LEFT : BRC.NOTCH_OFFSET_LEFT;
|
||||
bottomRow.connection.setOffsetInBlock(
|
||||
connX, this.info_.height + BRC.DARK_PATH_OFFSET);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Position the output connection on a block.
|
||||
* @param {!Blockly.BlockRendering.BottomRow} row The bottom row on the block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.Drawer.prototype.positionOutputConnection_ = function() {
|
||||
if (this.info_.hasOutputConnection) {
|
||||
this.block_.outputConnection.setOffsetInBlock(0, BRC.TAB_OFFSET_FROM_TOP);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2019 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Methods for graphically rendering a block as SVG.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
|
||||
//'use strict';
|
||||
goog.provide('Blockly.BlockRendering.Debug');
|
||||
goog.require('Blockly.BlockRendering.RenderInfo');
|
||||
goog.require('Blockly.BlockRendering.Highlighter');
|
||||
goog.require('BRC');
|
||||
goog.require('Blockly.BlockRendering.Measurable');
|
||||
|
||||
/**
|
||||
* An object that renders rectangles and dots for debugging rendering code.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Debug = function() {
|
||||
/**
|
||||
* An array of SVG elements that have been created by this object.
|
||||
* @type {Array.<!SVGElement>}
|
||||
*/
|
||||
this.debugElements_ = [];
|
||||
|
||||
/**
|
||||
* The SVG root of the block that is being rendered. Debug elements will
|
||||
* be attached to this root.
|
||||
* @type {!SVGElement}
|
||||
*/
|
||||
this.svgRoot_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all elements the this object created on the last pass.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Debug.prototype.clearElems = function() {
|
||||
for (var i = 0, elem; elem = this.debugElements_[i]; i++) {
|
||||
Blockly.utils.dom.removeNode(elem);
|
||||
}
|
||||
|
||||
this.debugElements_ = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw a debug rectangle for a spacer (empty) row.
|
||||
* @param {!Blockly.BlockRendering.Row} row The row to render
|
||||
* @param {number} cursorY The y position of the top of the row.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Debug.prototype.drawSpacerRow = function(row, cursorY) {
|
||||
this.debugElements_.push(Blockly.utils.dom.createSvgElement('rect',
|
||||
{
|
||||
'class': 'rowSpacerRect blockRenderDebug',
|
||||
'x': 0,
|
||||
'y': cursorY,
|
||||
'width': row.width,
|
||||
'height': row.height,
|
||||
},
|
||||
this.svgRoot_));
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw a debug rectangle for a horizontal spacer.
|
||||
* @param {!Blockly.BlockSvg.InRowSpacer} elem The spacer to render
|
||||
* @param {number} cursorX The x position of the left of the row.
|
||||
* @param {number} centerY The y position of the center of the row, vertically.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Debug.prototype.drawSpacerElem = function(elem, cursorX, centerY) {
|
||||
var yPos = centerY - elem.height / 2;
|
||||
this.debugElements_.push(Blockly.utils.dom.createSvgElement('rect',
|
||||
{
|
||||
'class': 'elemSpacerRect blockRenderDebug',
|
||||
'x': cursorX,
|
||||
'y': yPos,
|
||||
'width': elem.width,
|
||||
'height': 15,
|
||||
},
|
||||
this.svgRoot_));
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw a debug rectangle for an in-row element.
|
||||
* @param {!Blockly.BlockSvg.Measurable} elem The element to render
|
||||
* @param {number} cursorX The x position of the left of the row.
|
||||
* @param {number} centerY The y position of the center of the row, vertically.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Debug.prototype.drawRenderedElem = function(elem, cursorX, centerY) {
|
||||
var yPos = centerY - elem.height / 2;
|
||||
this.debugElements_.push(Blockly.utils.dom.createSvgElement('rect',
|
||||
{
|
||||
'class': 'rowRenderingRect blockRenderDebug',
|
||||
'x': cursorX,
|
||||
'y': yPos,
|
||||
'width': elem.width,
|
||||
'height': elem.height ,
|
||||
},
|
||||
this.svgRoot_));
|
||||
|
||||
if (elem.isInput) {
|
||||
this.drawConnection(elem.connection);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw a circle at the location of the given connection. Inputs and outputs
|
||||
* share the same colors, as do previous and next. When positioned correctly
|
||||
* a connected pair will look like a bullseye.
|
||||
* @param {Blockly.RenderedConnection} conn The connection to circle.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Debug.prototype.drawConnection = function(conn) {
|
||||
var colour;
|
||||
var size;
|
||||
var fill;
|
||||
if (conn.type == Blockly.INPUT_VALUE) {
|
||||
size = 4;
|
||||
colour = 'magenta';
|
||||
fill = 'none';
|
||||
} else if (conn.type == Blockly.OUTPUT_VALUE) {
|
||||
size = 2;
|
||||
colour = 'magenta';
|
||||
fill = colour;
|
||||
} else if (conn.type == Blockly.NEXT_STATEMENT) {
|
||||
size = 4;
|
||||
colour = 'goldenrod';
|
||||
fill = 'none';
|
||||
} else if (conn.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
size = 2;
|
||||
colour = 'goldenrod';
|
||||
fill = colour;
|
||||
}
|
||||
this.debugElements_.push(Blockly.utils.dom.createSvgElement('circle',
|
||||
{
|
||||
'class': 'blockRenderDebug',
|
||||
'cx': conn.offsetInBlock_.x,
|
||||
'cy': conn.offsetInBlock_.y,
|
||||
'r': size,
|
||||
'fill': fill,
|
||||
'stroke': colour,
|
||||
},
|
||||
this.svgRoot_));
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw a debug rectangle for a non-empty row.
|
||||
* @param {!Blockly.BlockSvg.Row} row The non-empty row to render.
|
||||
* @param {number} cursorY The y position of the top of the row.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Debug.prototype.drawRenderedRow = function(row, cursorY) {
|
||||
this.debugElements_.push(Blockly.utils.dom.createSvgElement('rect',
|
||||
{
|
||||
'class': 'elemRenderingRect blockRenderDebug',
|
||||
'x': 0,
|
||||
'y': cursorY ,
|
||||
'width': row.width,
|
||||
'height': row.height,
|
||||
},
|
||||
this.svgRoot_));
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw debug rectangles for a non-empty row and all of its subcomponents.
|
||||
* @param {!Blockly.BlockSvg.Row} row The non-empty row to render.
|
||||
* @param {number} cursorY The y position of the top of the row.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Debug.prototype.drawRowWithElements = function(row, cursorY) {
|
||||
var centerY = cursorY + row.height / 2;
|
||||
var cursorX = 0;
|
||||
for (var e = 0; e < row.elements.length; e++) {
|
||||
var elem = row.elements[e];
|
||||
if (elem.isSpacer()) {
|
||||
this.drawSpacerElem(elem, cursorX, centerY);
|
||||
} else {
|
||||
this.drawRenderedElem(elem, cursorX, centerY);
|
||||
}
|
||||
cursorX += elem.width;
|
||||
}
|
||||
this.drawRenderedRow(row, cursorY);
|
||||
};
|
||||
|
||||
/**
|
||||
* Do all of the work to draw debug information for the whole block.
|
||||
* @param {!Blockly.BlockSvg} block The block to draw debug information for.
|
||||
* @param {!Blockly.BlockRendering.RenderInfo} info Rendering information about
|
||||
* the block to debug.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Debug.prototype.drawDebug = function(block, info) {
|
||||
this.clearElems();
|
||||
this.svgRoot_ = block.getSvgRoot();
|
||||
var cursorY = 0;
|
||||
for (var r = 0; r < info.rows.length; r++) {
|
||||
var row = info.rows[r];
|
||||
if (row.isSpacer()) {
|
||||
this.drawSpacerRow(row, cursorY);
|
||||
} else {
|
||||
this.drawRowWithElements(row, cursorY);
|
||||
}
|
||||
cursorY += row.height;
|
||||
}
|
||||
|
||||
if (block.previousConnection) {
|
||||
this.drawConnection(block.previousConnection);
|
||||
}
|
||||
if (block.nextConnection) {
|
||||
this.drawConnection(block.nextConnection);
|
||||
}
|
||||
if (block.outputConnection) {
|
||||
this.drawConnection(block.outputConnection);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2019 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Methods for graphically rendering a block as SVG.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
|
||||
//'use strict';
|
||||
goog.provide('Blockly.BlockRendering.Highlighter');
|
||||
|
||||
goog.require('Blockly.BlockRendering.RenderInfo');
|
||||
goog.require('BRC');
|
||||
/* global BRC */
|
||||
goog.require('Blockly.BlockRendering.Measurable');
|
||||
|
||||
/**
|
||||
* An object that adds highlights to a block based on the given rendering
|
||||
* information.
|
||||
*
|
||||
* Highlighting is interesting because the highlights do not fully enclose the
|
||||
* block. Instead, they are positioned based on a light source in the top left.
|
||||
* This means that rendering highlights requires exact information about the
|
||||
* position of each part of the block. The resulting paths are not continuous
|
||||
* or closed paths. The highlights for tabs and notches are loosely based on
|
||||
* tab and notch shapes, but are not exactly the same.
|
||||
*
|
||||
* @param {!Blockly.BlockRendering.RenderInfo} info An object containing all
|
||||
* information needed to render this block.
|
||||
* @param {!Blockly.BlockSvg.PathObject} pathObject An object that stores all of
|
||||
* the block's paths before they are propagated to the page.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Highlighter = function(info, pathObject) {
|
||||
this.info_ = info;
|
||||
this.pathObject_ = pathObject;
|
||||
this.highlightSteps_ = this.pathObject_.highlightSteps;
|
||||
this.highlightInlineSteps_ = this.pathObject_.highlightInlineSteps;
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Highlighter.prototype.drawTopCorner = function(row) {
|
||||
for (var i = 0, elem; elem = row.elements[i]; i++) {
|
||||
if (elem.type === 'square corner') {
|
||||
this.highlightSteps_.push(BRC.START_POINT_HIGHLIGHT);
|
||||
} else if (elem.type === 'round corner') {
|
||||
this.highlightSteps_.push(this.info_.RTL ?
|
||||
BRC.TOP_LEFT_CORNER_START_HIGHLIGHT_RTL :
|
||||
BRC.TOP_LEFT_CORNER_START_HIGHLIGHT_LTR);
|
||||
this.highlightSteps_.push(BRC.TOP_LEFT_CORNER_HIGHLIGHT);
|
||||
} else if (elem.type === 'previous connection') {
|
||||
this.highlightSteps_.push(BRC.NOTCH_PATH_LEFT_HIGHLIGHT);
|
||||
} else if (elem.type === 'hat') {
|
||||
this.highlightSteps_.push(this.info_.RTL ?
|
||||
Blockly.BlockSvg.START_HAT_HIGHLIGHT_RTL :
|
||||
Blockly.BlockSvg.START_HAT_HIGHLIGHT_LTR);
|
||||
} else if (elem.isSpacer()) {
|
||||
this.highlightSteps_.push('h', elem.width - BRC.HIGHLIGHT_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
this.highlightSteps_.push('H', row.width - BRC.HIGHLIGHT_OFFSET);
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Highlighter.prototype.drawValueInput = function(row) {
|
||||
//var v = row.height - BRC.TAB_HEIGHT;
|
||||
|
||||
if (this.info_.RTL) {
|
||||
var aboveTabHeight = BRC.TAB_VERTICAL_OVERLAP - BRC.HIGHLIGHT_OFFSET;
|
||||
var belowTabHeight = row.height -
|
||||
(BRC.TAB_HEIGHT - BRC.TAB_VERTICAL_OVERLAP) +
|
||||
BRC.HIGHLIGHT_OFFSET;
|
||||
// Edge above tab.
|
||||
this.highlightSteps_.push('v', aboveTabHeight);
|
||||
// Highlight around back of tab.
|
||||
this.highlightSteps_.push(BRC.TAB_PATH_DOWN_HIGHLIGHT_RTL);
|
||||
// Edge below tab.
|
||||
this.highlightSteps_.push('v', belowTabHeight);
|
||||
} else {
|
||||
// Short highlight glint at bottom of tab.
|
||||
this.highlightSteps_.push('M', (row.width - 5) + ',' +
|
||||
(row.yPos + BRC.TAB_HEIGHT - 0.7));
|
||||
this.highlightSteps_.push('l', (BRC.TAB_WIDTH * 0.46) + ',-2.1');
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Highlighter.prototype.drawStatementInput = function(row) {
|
||||
var x = row.statementEdge;
|
||||
if (this.info_.RTL) {
|
||||
this.highlightSteps_.push('M',
|
||||
(x + BRC.DISTANCE_45_OUTSIDE) +
|
||||
',' + (row.yPos + BRC.DISTANCE_45_OUTSIDE));
|
||||
this.highlightSteps_.push(
|
||||
BRC.INNER_TOP_LEFT_CORNER_HIGHLIGHT_RTL);
|
||||
this.highlightSteps_.push('v',
|
||||
row.height - 2 * BRC.CORNER_RADIUS);
|
||||
this.highlightSteps_.push(
|
||||
BRC.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_RTL);
|
||||
} else {
|
||||
this.highlightSteps_.push('M',
|
||||
(x + BRC.DISTANCE_45_OUTSIDE) + ',' +
|
||||
(row.yPos + row.height - BRC.DISTANCE_45_OUTSIDE));
|
||||
this.highlightSteps_.push(
|
||||
BRC.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_LTR);
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Highlighter.prototype.drawRightSideRow = function(row) {
|
||||
if (row.followsStatement) {
|
||||
this.highlightSteps_.push('H', row.width);
|
||||
}
|
||||
if (this.info_.RTL) {
|
||||
this.highlightSteps_.push('H', row.width - BRC.HIGHLIGHT_OFFSET);
|
||||
this.highlightSteps_.push('v', row.height);
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Highlighter.prototype.drawBottomCorner = function(_row) {
|
||||
var height = this.info_.height;
|
||||
var elems = this.info_.bottomRow.elements;
|
||||
|
||||
if (this.info_.RTL) {
|
||||
this.highlightSteps_.push('V', height);
|
||||
}
|
||||
|
||||
for (var i = elems.length - 1; i >= 0; i--) {
|
||||
var elem = elems[i];
|
||||
if (elem.type === 'square corner') {
|
||||
if (!this.info_.RTL) {
|
||||
this.highlightSteps_.push('M',
|
||||
BRC.HIGHLIGHT_OFFSET + ',' + (height - BRC.HIGHLIGHT_OFFSET));
|
||||
}
|
||||
} else if (elem.type === 'round corner') {
|
||||
if (!this.info_.RTL) {
|
||||
this.highlightSteps_.push(BRC.BOTTOM_LEFT_CORNER_HIGHLIGHT_START +
|
||||
(height - Blockly.BlockSvg.DISTANCE_45_INSIDE) +
|
||||
BRC.BOTTOM_LEFT_CORNER_HIGHLIGHT_MID +
|
||||
(height - Blockly.BlockSvg.CORNER_RADIUS));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Highlighter.prototype.drawLeft = function() {
|
||||
if (this.info_.hasOutputConnection) {
|
||||
if (this.info_.RTL) {
|
||||
this.highlightSteps_.push(BRC.OUTPUT_CONNECTION_HIGHLIGHT_RTL);
|
||||
} else {
|
||||
this.highlightSteps_.push(BRC.OUTPUT_CONNECTION_HIGHLIGHT_LTR);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.info_.RTL) {
|
||||
if (this.info_.topRow.elements[0].isSquareCorner()) {
|
||||
this.highlightSteps_.push('V', BRC.HIGHLIGHT_OFFSET);
|
||||
} else {
|
||||
this.highlightSteps_.push('V', BRC.CORNER_RADIUS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Highlighter.prototype.drawInlineInput = function(input) {
|
||||
var width = input.width;
|
||||
var height = input.height;
|
||||
var x = input.xPos;
|
||||
var yPos = input.centerline - height / 2;
|
||||
var bottomHighlightWidth = width - BRC.TAB_WIDTH;
|
||||
|
||||
if (this.info_.RTL) {
|
||||
// TODO: Check if this is different when the inline input is populated.
|
||||
|
||||
var aboveTabHeight =
|
||||
BRC.TAB_OFFSET_FROM_TOP + BRC.TAB_VERTICAL_OVERLAP - BRC.HIGHLIGHT_OFFSET;
|
||||
|
||||
var belowTabHeight =
|
||||
height -
|
||||
(BRC.TAB_OFFSET_FROM_TOP + BRC.TAB_HEIGHT - BRC.TAB_VERTICAL_OVERLAP) +
|
||||
BRC.HIGHLIGHT_OFFSET;
|
||||
|
||||
var startX = x + BRC.TAB_WIDTH - BRC.HIGHLIGHT_OFFSET;
|
||||
var startY = yPos + BRC.HIGHLIGHT_OFFSET;
|
||||
|
||||
// Highlight right edge, around back of tab, and bottom.
|
||||
this.highlightInlineSteps_.push('M', startX + ',' + startY);
|
||||
// Right edge above tab.
|
||||
this.highlightInlineSteps_.push('v', aboveTabHeight);
|
||||
// Back of tab.
|
||||
this.highlightInlineSteps_.push(BRC.TAB_PATH_DOWN_HIGHLIGHT_RTL);
|
||||
// Right edge below tab.
|
||||
this.highlightInlineSteps_.push('v', belowTabHeight);
|
||||
// Bottom (horizontal).
|
||||
this.highlightInlineSteps_.push('h', bottomHighlightWidth);
|
||||
} else {
|
||||
// Highlight right edge, bottom.
|
||||
this.highlightInlineSteps_.push('M',
|
||||
(x + width + BRC.HIGHLIGHT_OFFSET) + ',' +
|
||||
(yPos + BRC.HIGHLIGHT_OFFSET));
|
||||
this.highlightInlineSteps_.push('v', height);
|
||||
this.highlightInlineSteps_.push('h ', -bottomHighlightWidth);
|
||||
// Short highlight glint at bottom of tab.
|
||||
// Bad: reference to Blockly.BlockSvg
|
||||
this.highlightInlineSteps_.push('M',
|
||||
(x + 2.9) + ',' + (yPos + Blockly.BlockSvg.INLINE_PADDING_Y +
|
||||
BRC.TAB_HEIGHT - 0.7));
|
||||
this.highlightInlineSteps_.push('l',
|
||||
(BRC.TAB_WIDTH * 0.46) + ',-2.1');
|
||||
}
|
||||
};
|
||||
678
core/renderers/block_rendering_rewrite/block_render_info.js
Normal file
678
core/renderers/block_rendering_rewrite/block_render_info.js
Normal file
@@ -0,0 +1,678 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2019 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Methods for graphically rendering a block as SVG.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
|
||||
//'use strict';
|
||||
|
||||
goog.provide('Blockly.BlockRendering.RenderInfo');
|
||||
|
||||
goog.require('BRC');
|
||||
/* global BRC */
|
||||
goog.require('Blockly.BlockRendering.Measurable');
|
||||
|
||||
/**
|
||||
* An object containing all sizing information needed to draw this block.
|
||||
*
|
||||
* This measure pass does not propagate changes to the block (although fields
|
||||
* may choose to rerender when getSize() is called). However, calling it
|
||||
* repeatedly may be expensive.
|
||||
*
|
||||
* @param {!Blockly.BlockSvg} block The block to measure.
|
||||
* @constructor
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo = function(block) {
|
||||
this.block_ = block;
|
||||
|
||||
/**
|
||||
* Whether the block has an output connection.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.hasOutputConnection = !!block.outputConnection;
|
||||
|
||||
/**
|
||||
* Whether the block should be rendered as a single line, either because it's
|
||||
* inline or because it has been collapsed.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isInline = block.getInputsInline() && !block.isCollapsed();
|
||||
|
||||
/**
|
||||
* Whether the block is an insertion marker. Insertion markers are the same
|
||||
* shape as normal blocks, but don't show fields.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isInsertionMarker = block.isInsertionMarker();
|
||||
|
||||
/**
|
||||
* True if the block should be rendered right-to-left.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.RTL = block.RTL;
|
||||
|
||||
/**
|
||||
* The height of the rendered block, including child blocks.
|
||||
* @type {number}
|
||||
*/
|
||||
this.height = 0;
|
||||
|
||||
/**
|
||||
* The width of the rendered block, including child blocks.
|
||||
* @type {number}
|
||||
*/
|
||||
this.widthWithChildren = 0;
|
||||
|
||||
/**
|
||||
* The width of the rendered block, excluding child blocks. This is the right
|
||||
* edge of the block when rendered LTR.
|
||||
* @type {number}
|
||||
*/
|
||||
this.width = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
this.statementEdge = 0;
|
||||
|
||||
/**
|
||||
* An array of Row objects containing sizing information.
|
||||
* @type {Array}
|
||||
*/
|
||||
this.rows = [];
|
||||
|
||||
this.topRow = null;
|
||||
this.bottomRow = null;
|
||||
|
||||
this.measure_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate and return an object containing all sizing information needed to
|
||||
* draw this block.
|
||||
*
|
||||
* This measure pass does not propagate changes to the block (although fields
|
||||
* may choose to rerender when getSize() is called). However, calling it
|
||||
* repeatedly may be expensive.
|
||||
*
|
||||
* @param {!Blockly.BlockSvg} block The block to measure.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.measure_ = function() {
|
||||
this.createRows_();
|
||||
this.addElemSpacing_();
|
||||
this.computeBounds_();
|
||||
this.alignRowElements_();
|
||||
this.addRowSpacing_();
|
||||
this.finalize_();
|
||||
|
||||
console.log(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create rows of Measurable objects representing all renderable parts of the
|
||||
* block.
|
||||
* @param {!Blockly.BlockSvg} block The block to create rows from.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.createRows_ = function() {
|
||||
this.createTopRow_();
|
||||
this.rows.push(this.topRow);
|
||||
|
||||
var activeRow = new Blockly.BlockRendering.Row();
|
||||
|
||||
// Icons always go on the first row, before anything else.
|
||||
var icons = this.block_.getIcons();
|
||||
if (icons.length) {
|
||||
for (var i = 0; i < icons.length; i++) {
|
||||
activeRow.elements.push(
|
||||
new Blockly.BlockRendering.Icon(icons[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Loop across all of the inputs on the block, creating objects for anything
|
||||
// that needs to be rendered and breaking the block up into visual rows.
|
||||
for (var i = 0; i < this.block_.inputList.length; i++) {
|
||||
var input = this.block_.inputList[i];
|
||||
if (this.shouldStartNewRow_(input, this.block_.inputList[i - 1])) {
|
||||
// Finish this row and create a new one.
|
||||
this.rows.push(activeRow);
|
||||
activeRow = new Blockly.BlockRendering.Row();
|
||||
}
|
||||
|
||||
// All of the fields in an input go on the same row.
|
||||
for (var f = 0; f < input.fieldRow.length; f++) {
|
||||
var field = input.fieldRow[f];
|
||||
activeRow.elements.push(new Blockly.BlockRendering.Field(field, input));
|
||||
}
|
||||
this.addInput_(input, activeRow);
|
||||
}
|
||||
|
||||
if (activeRow.elements.length) {
|
||||
this.rows.push(activeRow);
|
||||
}
|
||||
this.createBottomRow_();
|
||||
this.rows.push(this.bottomRow);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the top row and fill the elements list with all non-spacer elements
|
||||
* created.
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.createTopRow_ = function() {
|
||||
var hasHat = this.block_.hat ? this.block_.hat === 'cap' : Blockly.BlockSvg.START_HAT;
|
||||
var hasPrevious = !!this.block_.previousConnection;
|
||||
var prevBlock = this.block_.getPreviousBlock();
|
||||
var squareCorner = !!this.block_.outputConnection ||
|
||||
hasHat || (prevBlock && prevBlock.getNextBlock() == this.block_);
|
||||
this.topRow = new Blockly.BlockRendering.TopRow(this.block_);
|
||||
|
||||
if (squareCorner) {
|
||||
this.topRow.elements.push(new Blockly.BlockRendering.SquareCorner());
|
||||
} else {
|
||||
this.topRow.elements.push(new Blockly.BlockRendering.RoundCorner());
|
||||
}
|
||||
|
||||
if (hasHat) {
|
||||
this.topRow.elements.push(new Blockly.BlockRendering.Hat());
|
||||
} else if (hasPrevious) {
|
||||
this.topRow.elements.push(new Blockly.BlockRendering.PreviousConnection());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the bottom row and fill the elements list with all non-spacer elements
|
||||
* created.
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.createBottomRow_ = function() {
|
||||
var squareCorner = !!this.block_.outputConnection || !!this.block_.getNextBlock();
|
||||
this.bottomRow = new Blockly.BlockRendering.BottomRow(this.block_);
|
||||
|
||||
if (squareCorner) {
|
||||
this.bottomRow.elements.push(new Blockly.BlockRendering.SquareCorner());
|
||||
} else {
|
||||
this.bottomRow.elements.push(new Blockly.BlockRendering.RoundCorner());
|
||||
}
|
||||
|
||||
if (this.bottomRow.hasNextConnection) {
|
||||
this.bottomRow.elements.push(new Blockly.BlockRendering.NextConnection());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add an input element to the active row, if needed, and record the type of the
|
||||
* input on the row.
|
||||
* @param {!Blockly.Input} input The input to record information about.
|
||||
* @param {!Blockly.BlockRendering.Row} activeRow The row that is currently being
|
||||
* populated.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.addInput_ = function(input, activeRow) {
|
||||
// Non-dummy inputs have visual representations onscreen.
|
||||
if (this.isInline && input.type == Blockly.INPUT_VALUE) {
|
||||
activeRow.elements.push(new Blockly.BlockRendering.InlineInput(input));
|
||||
activeRow.hasInlineInput = true;
|
||||
} else if (input.type == Blockly.NEXT_STATEMENT) {
|
||||
activeRow.elements.push(new Blockly.BlockRendering.StatementInput(input));
|
||||
activeRow.hasStatement = true;
|
||||
} else if (input.type == Blockly.INPUT_VALUE) {
|
||||
activeRow.elements.push(new Blockly.BlockRendering.ExternalValueInput(input));
|
||||
activeRow.hasExternalInput = true;
|
||||
} else if (input.type == Blockly.DUMMY_INPUT) {
|
||||
// Dummy inputs have no visual representation, but the information is still
|
||||
// important.
|
||||
activeRow.hasDummyInput = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Decide whether to start a new row between the two Blockly.Inputs.
|
||||
* @param {!Blockly.Input} input The first input to consider
|
||||
* @param {Blockly.Input} lastInput The input that follows.
|
||||
* @return {boolean} True if the next input should be rendered on a new row.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.shouldStartNewRow_ = function(input, lastInput) {
|
||||
// If this is the first input, just add to the existing row.
|
||||
// That row is either empty or has some icons in it.
|
||||
if (!lastInput) {
|
||||
return false;
|
||||
}
|
||||
// A statement input always gets a new row.
|
||||
if (input.type == Blockly.NEXT_STATEMENT) {
|
||||
return true;
|
||||
}
|
||||
// Value and dummy inputs get new row if inputs are not inlined.
|
||||
if (input.type == Blockly.INPUT_VALUE || input.type == Blockly.DUMMY_INPUT) {
|
||||
return !this.isInline;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add horizontal spacing between and around elements within each row.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.addElemSpacing_ = function() {
|
||||
for (var r = 0; r < this.rows.length; r++) {
|
||||
var row = this.rows[r];
|
||||
var oldElems = row.elements;
|
||||
row.elements = [];
|
||||
// No spacing needed before the corner on the top row or the bottom row.
|
||||
if (row.type != 'top row' && row.type != 'bottom row') {
|
||||
// There's a spacer before the first element in the row.
|
||||
row.elements.push(new Blockly.BlockRendering.InRowSpacer(
|
||||
this.getInRowSpacing_(null, oldElems[0])));
|
||||
}
|
||||
|
||||
for (var e = 0; e < oldElems.length; e++) {
|
||||
row.elements.push(oldElems[e]);
|
||||
var spacing = this.getInRowSpacing_(oldElems[e], oldElems[e + 1]);
|
||||
row.elements.push(new Blockly.BlockRendering.InRowSpacer(spacing));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the width of a spacer element in a row based on the previous and
|
||||
* next elements in that row. For instance, extra padding is added between two
|
||||
* editable fields.
|
||||
* @param {Blockly.BlockRendering.Measurable} prev The element before the
|
||||
* spacer.
|
||||
* @param {Blockly.BlockRendering.Measurable} next The element after the spacer.
|
||||
* @return {number} The size of the spacing between the two elements.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.getInRowSpacing_ = function(prev, next) {
|
||||
if (!prev) {
|
||||
// Between an editable field and the beginning of the row.
|
||||
if (next.isField() && next.isEditable) {
|
||||
return BRC.MEDIUM_PADDING;
|
||||
}
|
||||
// Inline input at the beginning of the row.
|
||||
if (next.isInput && next.isInlineInput()) {
|
||||
return BRC.MEDIUM_LARGE_PADDING;
|
||||
}
|
||||
if (next.isStatementInput()) {
|
||||
return BRC.STATEMENT_INPUT_PADDING_LEFT;
|
||||
}
|
||||
// Anything else at the beginning of the row.
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
|
||||
// Spacing between a field or icon and the end of the row.
|
||||
if (!prev.isInput && !next) {
|
||||
// Between an editable field and the end of the row.
|
||||
if (prev.isField() && prev.isEditable) {
|
||||
return BRC.MEDIUM_PADDING;
|
||||
}
|
||||
// Padding at the end of an icon-only row to make the block shape clearer.
|
||||
if (prev.isIcon()) {
|
||||
return (BRC.LARGE_PADDING * 2) + 1;
|
||||
}
|
||||
if (prev.isHat()){
|
||||
return BRC.NO_PADDING;
|
||||
}
|
||||
// Establish a minimum width for a block with a previous or next connection.
|
||||
if (prev.isPreviousConnection() || prev.isNextConnection()) {
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
// Between rounded corner and the end of the row.
|
||||
if (prev.isRoundedCorner()) {
|
||||
return BRC.MIN_BLOCK_WIDTH;
|
||||
}
|
||||
// Between noneditable fields and icons and the end of the row.
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
|
||||
// Between inputs and the end of the row.
|
||||
if (prev.isInput && !next) {
|
||||
if (prev.isExternalInput()) {
|
||||
return BRC.NO_PADDING;
|
||||
} else if (prev.isInlineInput()) {
|
||||
return BRC.LARGE_PADDING;
|
||||
} else if (prev.isStatementInput()) {
|
||||
return BRC.NO_PADDING;
|
||||
}
|
||||
}
|
||||
|
||||
// Spacing between a field or icon and an input.
|
||||
if (!prev.isInput && next.isInput) {
|
||||
// Between an editable field and an input.
|
||||
if (prev.isEditable) {
|
||||
if (next.isInlineInput()) {
|
||||
return BRC.SMALL_PADDING;
|
||||
} else if (next.isExternalInput()) {
|
||||
return BRC.SMALL_PADDING;
|
||||
}
|
||||
} else {
|
||||
if (next.isInlineInput()) {
|
||||
return BRC.MEDIUM_LARGE_PADDING;
|
||||
} else if (next.isExternalInput()) {
|
||||
return BRC.MEDIUM_LARGE_PADDING;
|
||||
} else if (next.isStatementInput()) {
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
}
|
||||
return BRC.LARGE_PADDING - 1;
|
||||
}
|
||||
|
||||
// Spacing between an icon and an icon or field.
|
||||
if (prev.isIcon() && !next.isInput) {
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
|
||||
// Spacing between an inline input and a field.
|
||||
if (prev.isInlineInput() && !next.isInput) {
|
||||
// Editable field after inline input.
|
||||
if (next.isEditable) {
|
||||
return BRC.MEDIUM_PADDING;
|
||||
} else {
|
||||
// Noneditable field after inline input.
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
}
|
||||
|
||||
if (prev.isSquareCorner()) {
|
||||
// Spacing between a hat and a corner
|
||||
if (next.isHat()) {
|
||||
return BRC.NO_PADDING;
|
||||
}
|
||||
// Spacing between a square corner and a previous or next connection
|
||||
if (next.isPreviousConnection()) {
|
||||
return BRC.NOTCH_OFFSET_LEFT;
|
||||
} else if (next.isNextConnection()) {
|
||||
// Next connections are shifted slightly to the left (in both LTR and RTL)
|
||||
// to make the dark path under the previous connection show through.
|
||||
return BRC.NOTCH_OFFSET_LEFT + (this.RTL ? 0.5 : - 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
// Spacing between a rounded corner and a previous or next connection
|
||||
if (prev.isRoundedCorner()){
|
||||
if (next.isPreviousConnection()) {
|
||||
return BRC.NOTCH_OFFSET_ROUNDED_CORNER_PREV;
|
||||
} else if (next.isNextConnection()) {
|
||||
// Next connections are shifted slightly to the left (in both LTR and RTL)
|
||||
// to make the dark path under the previous connection show through.
|
||||
return BRC.NOTCH_OFFSET_ROUNDED_CORNER_PREV + (this.RTL ? 0.5 : - 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
// Spacing between two fields of the same editability.
|
||||
if (!prev.isInput && !next.isInput && (prev.isEditable == next.isEditable)) {
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
|
||||
return BRC.MEDIUM_PADDING;
|
||||
};
|
||||
|
||||
/**
|
||||
* Figure out where the right edge of the block and right edge of statement inputs
|
||||
* should be placed.
|
||||
* TODO: More cleanup.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.computeBounds_ = function() {
|
||||
var widestStatementRowFields = 0;
|
||||
var blockWidth = 0;
|
||||
var widestRowWithConnectedBlocks = 0;
|
||||
for (var r = 0; r < this.rows.length; r++) {
|
||||
var row = this.rows[r];
|
||||
row.measure();
|
||||
if (!row.hasStatement) {
|
||||
blockWidth = Math.max(blockWidth, row.width);
|
||||
}
|
||||
if (row.hasStatement) {
|
||||
var statementInput = row.getLastInput();
|
||||
var innerWidth = row.width - statementInput.width;
|
||||
widestStatementRowFields = Math.max(widestStatementRowFields, innerWidth);
|
||||
}
|
||||
widestRowWithConnectedBlocks =
|
||||
Math.max(widestRowWithConnectedBlocks, row.widthWithConnectedBlocks);
|
||||
}
|
||||
|
||||
|
||||
this.statementEdge = widestStatementRowFields;
|
||||
|
||||
if (widestStatementRowFields) {
|
||||
this.width =
|
||||
Math.max(blockWidth,
|
||||
widestStatementRowFields + BRC.NOTCH_WIDTH * 2);
|
||||
} else {
|
||||
this.width = blockWidth;
|
||||
}
|
||||
|
||||
for (var r = 0; r < this.rows.length; r++) {
|
||||
var row = this.rows[r];
|
||||
if (row.hasStatement) {
|
||||
row.statementEdge = this.statementEdge;
|
||||
}
|
||||
}
|
||||
|
||||
this.widthWithChildren =
|
||||
Math.max(blockWidth, widestRowWithConnectedBlocks);
|
||||
};
|
||||
|
||||
/**
|
||||
* Extra spacing may be necessary to make sure that the right sides of all
|
||||
* rows line up. This can only be calculated after a first pass to calculate
|
||||
* the sizes of all rows.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.alignRowElements_ = function() {
|
||||
for (var r = 0; r < this.rows.length; r++) {
|
||||
var row = this.rows[r];
|
||||
if (!row.hasStatement && !row.hasInlineInput) {
|
||||
var currentWidth = row.width;
|
||||
var desiredWidth = this.width;
|
||||
if (row.type === 'bottom row' && row.hasFixedWidth) {
|
||||
desiredWidth = BRC.MAX_BOTTOM_WIDTH;
|
||||
}
|
||||
var missingSpace = desiredWidth - currentWidth;
|
||||
if (missingSpace) {
|
||||
this.addAlignmentPadding_(row, missingSpace);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify the given row to add the given amount of padding around its fields.
|
||||
* The exact location of the padding is based on the alignment property of the
|
||||
* last input in the field.
|
||||
* @param {Blockly.BlockRendering.Row} row The row to add padding to.
|
||||
* @param {number} missingSpace How much padding to add.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.addAlignmentPadding_ = function(row, missingSpace) {
|
||||
var elems = row.elements;
|
||||
var input = row.getLastInput();
|
||||
if (input) {
|
||||
var firstSpacer = row.getFirstSpacer();
|
||||
var lastSpacer = row.getLastSpacer();
|
||||
if (row.hasExternalInput) {
|
||||
// Get the spacer right before the input socket.
|
||||
lastSpacer = elems[elems.length - 3];
|
||||
}
|
||||
// Decide where the extra padding goes.
|
||||
if (input.align == Blockly.ALIGN_LEFT) {
|
||||
// Add padding to the end of the row.
|
||||
lastSpacer.width += missingSpace;
|
||||
} else if (input.align == Blockly.ALIGN_CENTRE) {
|
||||
// Split the padding between the beginning and end of the row.
|
||||
firstSpacer.width += missingSpace / 2;
|
||||
lastSpacer.width += missingSpace / 2;
|
||||
} else if (input.align == Blockly.ALIGN_RIGHT) {
|
||||
// Add padding at the beginning of the row.
|
||||
firstSpacer.width += missingSpace;
|
||||
}
|
||||
row.width += missingSpace;
|
||||
// Top and bottom rows are always left aligned.
|
||||
} else if (row.type === 'top row' || row.type === 'bottom row') {
|
||||
row.getLastSpacer().width += missingSpace;
|
||||
row.width += missingSpace;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add spacers between rows and set their sizes.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.addRowSpacing_ = function() {
|
||||
var oldRows = this.rows;
|
||||
this.rows = [];
|
||||
|
||||
for (var r = 0; r < oldRows.length; r++) {
|
||||
this.rows.push(oldRows[r]);
|
||||
if (r !== oldRows.length - 1) {
|
||||
this.rows.push(this.makeSpacerRow_(oldRows[r], oldRows[r + 1]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a spacer row to go between prev and next, and set its size.
|
||||
* @param {?Blockly.BlockRendering.Measurable} prev The previous row, or null.
|
||||
* @param {?Blockly.BlockRendering.Measurable} next The next row, or null.
|
||||
* @return {!Blockly.BlockSvg.BetweenRowSpacer} The newly created spacer row.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.makeSpacerRow_ = function(prev, next) {
|
||||
var height = this.getSpacerRowHeight_(prev, next);
|
||||
var width = this.getSpacerRowWidth_(prev, next);
|
||||
var spacer = new Blockly.BlockRendering.BetweenRowSpacer(height, width);
|
||||
if (prev.hasStatement) {
|
||||
spacer.followsStatement = true;
|
||||
}
|
||||
return spacer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the width of a spacer row. Almost all spacers will be the full
|
||||
* width of the block, but there are some exceptions (e.g. the small spacer row
|
||||
* after a statement input)
|
||||
* @param {Blockly.BlockRendering.Row} prev The row before the spacer.
|
||||
* @param {Blockly.BlockRendering.Row} next The row after the spacer.
|
||||
* @return {number} The desired width of the spacer row between these two rows.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.getSpacerRowWidth_ = function(prev, next) {
|
||||
// The width of the spacer before the bottom row should be the same as the
|
||||
// bottom row.
|
||||
if (next.type === 'bottom row'
|
||||
&& next.hasFixedWidth) {
|
||||
return next.width;
|
||||
}
|
||||
return this.width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the height of a spacer row.
|
||||
* @param {Blockly.BlockRendering.Row} prev The row before the spacer.
|
||||
* @param {Blockly.BlockRendering.Row} next The row after the spacer.
|
||||
* @return {number} The desired height of the spacer row between these two rows.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.getSpacerRowHeight_ = function(prev, next) {
|
||||
// If we have an empty block add a spacer to increase the height
|
||||
if (prev.type === 'top row' && next.type === 'bottom row') {
|
||||
return BRC.EMPTY_BLOCK_SPACER_HEIGHT;
|
||||
}
|
||||
// Top and bottom rows act as a spacer so we don't need any extra padding
|
||||
if (prev.type === 'top row' || next.type === 'bottom row') {
|
||||
return BRC.NO_PADDING;
|
||||
}
|
||||
if (prev.hasExternalInput && next.hasExternalInput) {
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
if (!prev.hasStatement && next.hasStatement) {
|
||||
return BRC.BETWEEN_STATEMENT_PADDING_Y;
|
||||
}
|
||||
if (prev.hasStatement && next.hasStatement) {
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
if (next.hasDummyInput) {
|
||||
return BRC.LARGE_PADDING;
|
||||
}
|
||||
return BRC.MEDIUM_PADDING;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the centerline of an element in a rendered row.
|
||||
* @param {Blockly.BlockRendering.Row} row The row containing the element.
|
||||
* @param {Blockly.BlockRendering.Measurable} elem The element to place.
|
||||
* @return {number} The desired centerline of the given element, as an offset
|
||||
* from the top left of the block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.RenderInfo.prototype.getElemCenterline_ = function(row, elem) {
|
||||
var result = row.yPos;
|
||||
if (elem.isField()) {
|
||||
result += (elem.height / 2);
|
||||
if (row.hasInlineInput || row.hasStatement) {
|
||||
result += BRC.TALL_INPUT_FIELD_OFFSET_Y;
|
||||
}
|
||||
} else if (elem.isInlineInput()) {
|
||||
result += elem.height / 2;
|
||||
} else {
|
||||
result += (row.height / 2);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
/**
|
||||
* Make any final changes to the rendering information object. In particular,
|
||||
* store the y position of each row, and record the height of the full block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockRendering.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 r = 0; r < this.rows.length; r++) {
|
||||
var row = this.rows[r];
|
||||
row.yPos = yCursor;
|
||||
var xCursor = 0;
|
||||
if (!(row.isSpacer())) {
|
||||
for (var e = 0; e < row.elements.length; e++) {
|
||||
var elem = row.elements[e];
|
||||
elem.xPos = xCursor;
|
||||
elem.centerline = this.getElemCenterline_(row, elem);
|
||||
xCursor += elem.width;
|
||||
}
|
||||
}
|
||||
yCursor += row.height;
|
||||
}
|
||||
this.blockBottom = yCursor;
|
||||
|
||||
// Add padding to the bottom row if block height is less than minimum
|
||||
if (yCursor < BRC.MIN_BLOCK_HEIGHT) {
|
||||
this.bottomRow.height += BRC.MIN_BLOCK_HEIGHT - yCursor;
|
||||
yCursor = BRC.MIN_BLOCK_HEIGHT;
|
||||
}
|
||||
|
||||
this.height = yCursor;
|
||||
};
|
||||
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2019 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Methods for graphically rendering a block as SVG.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
|
||||
//'use strict';
|
||||
goog.provide('BRC');
|
||||
/* global BRC */
|
||||
|
||||
goog.require('Blockly.utils.Paths');
|
||||
|
||||
|
||||
BRC.NO_PADDING = 0;
|
||||
BRC.SMALL_PADDING = 3;
|
||||
BRC.MEDIUM_PADDING = 5;
|
||||
BRC.MEDIUM_LARGE_PADDING = 8;
|
||||
BRC.LARGE_PADDING = 10;
|
||||
|
||||
// Offset from the top of the row for placing fields on inline input rows
|
||||
// and statement input rows.
|
||||
// Matches existing rendering (in 2019).
|
||||
BRC.TALL_INPUT_FIELD_OFFSET_Y = BRC.MEDIUM_PADDING;
|
||||
|
||||
BRC.HIGHLIGHT_OFFSET = 0.5;
|
||||
|
||||
// The dark/shadow path in classic rendering is the same as the normal block
|
||||
// path, but translated down one and right one.
|
||||
BRC.DARK_PATH_OFFSET = 1;
|
||||
|
||||
BRC.TAB_HEIGHT = 15;
|
||||
|
||||
BRC.TAB_OFFSET_FROM_TOP = 5;
|
||||
|
||||
BRC.TAB_VERTICAL_OVERLAP = 2.5;
|
||||
|
||||
BRC.TAB_WIDTH = 8;
|
||||
|
||||
BRC.NOTCH_WIDTH = 15;
|
||||
BRC.NOTCH_HEIGHT = 4;
|
||||
|
||||
// This is the minimum width of a block measuring from the end of a rounded
|
||||
// corner
|
||||
BRC.MIN_BLOCK_WIDTH = 12;
|
||||
|
||||
BRC.EMPTY_BLOCK_SPACER_HEIGHT = 16;
|
||||
|
||||
|
||||
// Offset from the left side of a block or the inside of a statement input to
|
||||
// the left side of the notch.
|
||||
BRC.NOTCH_OFFSET_LEFT = BRC.NOTCH_WIDTH;
|
||||
|
||||
// This is the width from where a rounded corner ends to where a previous
|
||||
// connection starts.
|
||||
BRC.NOTCH_OFFSET_ROUNDED_CORNER_PREV = 7;
|
||||
|
||||
// This is the offset from the vertical part of a statement input
|
||||
// to where to start the notch, which is on the right side in LTR.
|
||||
BRC.NOTCH_OFFSET_RIGHT = BRC.NOTCH_OFFSET_LEFT + BRC.NOTCH_WIDTH;
|
||||
|
||||
BRC.STATEMENT_BOTTOM_SPACER = 5;
|
||||
BRC.STATEMENT_INPUT_PADDING_LEFT = 20;
|
||||
BRC.BETWEEN_STATEMENT_PADDING_Y = 4;
|
||||
|
||||
// This is the max width of a bottom row that follows a statement input and
|
||||
// has inputs inline.
|
||||
BRC.MAX_BOTTOM_WIDTH = 66.5;
|
||||
|
||||
/**
|
||||
* Rounded corner radius.
|
||||
* @const
|
||||
*/
|
||||
BRC.CORNER_RADIUS = 8;
|
||||
/**
|
||||
* Height of the top hat.
|
||||
* @const
|
||||
*/
|
||||
BRC.START_HAT_HEIGHT = 15;
|
||||
|
||||
BRC.START_HAT_WIDTH = 100;
|
||||
|
||||
BRC.SPACER_DEFAULT_HEIGHT = 15;
|
||||
|
||||
BRC.MIN_BLOCK_HEIGHT = 24;
|
||||
|
||||
BRC.EMPTY_INLINE_INPUT_WIDTH = 22.5;
|
||||
|
||||
BRC.EMPTY_INLINE_INPUT_HEIGHT = 26;
|
||||
|
||||
BRC.EXTERNAL_VALUE_INPUT_WIDTH = 10;
|
||||
|
||||
/**
|
||||
* The height of an empty statement input. Note that in the old rendering this
|
||||
* varies slightly depending on whether the block has external or inline inputs.
|
||||
* In the new rendering this is consistent. It seems unlikely that the old
|
||||
* behaviour was intentional.
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
BRC.EMPTY_STATEMENT_INPUT_HEIGHT = BRC.MIN_BLOCK_HEIGHT;
|
||||
|
||||
BRC.EMPTY_STATEMENT_INPUT_WIDTH = 32;
|
||||
|
||||
BRC.POPULATED_STATEMENT_INPUT_WIDTH = 25;
|
||||
|
||||
|
||||
BRC.START_POINT = Blockly.utils.Paths.moveBy(0, 0);
|
||||
|
||||
BRC.START_POINT_HIGHLIGHT =
|
||||
Blockly.utils.Paths.moveBy(BRC.HIGHLIGHT_OFFSET, BRC.HIGHLIGHT_OFFSET);
|
||||
|
||||
/**
|
||||
* Distance from shape edge to intersect with a curved corner at 45 degrees.
|
||||
* Applies to highlighting on around the inside of a curve.
|
||||
* @const
|
||||
*/
|
||||
BRC.DISTANCE_45_INSIDE = (1 - Math.SQRT1_2) *
|
||||
(BRC.CORNER_RADIUS - BRC.HIGHLIGHT_OFFSET) + BRC.HIGHLIGHT_OFFSET;
|
||||
|
||||
/**
|
||||
* Distance from shape edge to intersect with a curved corner at 45 degrees.
|
||||
* Applies to highlighting on around the outside of a curve.
|
||||
* @const
|
||||
*/
|
||||
BRC.DISTANCE_45_OUTSIDE = (1 - Math.SQRT1_2) *
|
||||
(BRC.CORNER_RADIUS + BRC.HIGHLIGHT_OFFSET) - BRC.HIGHLIGHT_OFFSET;
|
||||
|
||||
/**
|
||||
* SVG path for drawing a horizontal puzzle tab from top to bottom.
|
||||
* @const
|
||||
*/
|
||||
BRC.TAB_PATH_DOWN = 'c 0,10 -' + BRC.TAB_WIDTH +
|
||||
',-8 -' + BRC.TAB_WIDTH + ',7.5 s ' +
|
||||
BRC.TAB_WIDTH + ',-2.5 ' + BRC.TAB_WIDTH + ',7.5';
|
||||
|
||||
|
||||
/**
|
||||
* SVG path for drawing a horizontal puzzle tab from top to bottom with
|
||||
* highlighting from the upper-right.
|
||||
* @const
|
||||
*/
|
||||
BRC.TAB_PATH_DOWN_HIGHLIGHT_RTL = 'm -' +
|
||||
(BRC.TAB_WIDTH * 0.97) + ',2.5 q -' +
|
||||
(BRC.TAB_WIDTH * 0.05) + ',10 ' +
|
||||
(BRC.TAB_WIDTH * 0.3) + ',9.5 m ' +
|
||||
(BRC.TAB_WIDTH * 0.67) + ',-1.9';
|
||||
|
||||
/**
|
||||
* SVG path for drawing a horizontal puzzle tab from bottom to top.
|
||||
* @const
|
||||
*/
|
||||
BRC.TAB_PATH_UP = 'c 0,-10 -' + BRC.TAB_WIDTH +
|
||||
',8 -' + BRC.TAB_WIDTH + ',-7.5 s ' +
|
||||
BRC.TAB_WIDTH + ',2.5 ' + BRC.TAB_WIDTH + ',-7.5';
|
||||
|
||||
/**
|
||||
* Path of the top hat's curve.
|
||||
* @const
|
||||
*/
|
||||
BRC.START_HAT_PATH =
|
||||
Blockly.utils.Paths.curve('c',
|
||||
[
|
||||
Blockly.utils.Paths.point(30, -BRC.START_HAT_HEIGHT),
|
||||
Blockly.utils.Paths.point(70, -BRC.START_HAT_HEIGHT),
|
||||
Blockly.utils.Paths.point(BRC.START_HAT_WIDTH, 0)
|
||||
]);
|
||||
/**
|
||||
* SVG path for drawing next/previous notch from left to right.
|
||||
* @const
|
||||
*/
|
||||
BRC.NOTCH_PATH_LEFT = 'l 6,4 3,0 6,-4';
|
||||
|
||||
/**
|
||||
* SVG path for drawing next/previous notch from left to right with
|
||||
* highlighting.
|
||||
* @const
|
||||
*/
|
||||
BRC.NOTCH_PATH_LEFT_HIGHLIGHT =
|
||||
'h ' + BRC.HIGHLIGHT_OFFSET + ' ' + BRC.NOTCH_PATH_LEFT;
|
||||
|
||||
/**
|
||||
* SVG path for drawing next/previous notch from right to left.
|
||||
* @const
|
||||
*/
|
||||
BRC.NOTCH_PATH_RIGHT = 'l -6,4 -3,0 -6,-4';
|
||||
|
||||
/**
|
||||
* SVG path for drawing the top-left corner of a statement input.
|
||||
* Includes the top notch, a horizontal space, and the rounded inside corner.
|
||||
* @const
|
||||
*/
|
||||
BRC.INNER_TOP_LEFT_CORNER =
|
||||
BRC.NOTCH_PATH_RIGHT + ' h -' +
|
||||
(BRC.NOTCH_WIDTH - BRC.CORNER_RADIUS) +
|
||||
' a ' + BRC.CORNER_RADIUS + ',' +
|
||||
BRC.CORNER_RADIUS + ' 0 0,0 -' +
|
||||
BRC.CORNER_RADIUS + ',' +
|
||||
BRC.CORNER_RADIUS;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the bottom-left corner of a statement input.
|
||||
* Includes the rounded inside corner.
|
||||
* @const
|
||||
*/
|
||||
BRC.INNER_BOTTOM_LEFT_CORNER =
|
||||
Blockly.utils.Paths.arc('a', '0 0,0',
|
||||
BRC.CORNER_RADIUS,
|
||||
Blockly.utils.Paths.point(BRC.CORNER_RADIUS, BRC.CORNER_RADIUS));
|
||||
|
||||
|
||||
/**
|
||||
* SVG path for drawing highlight on the top-left corner of a statement
|
||||
* input in RTL.
|
||||
* @const
|
||||
*/
|
||||
BRC.INNER_TOP_LEFT_CORNER_HIGHLIGHT_RTL =
|
||||
Blockly.utils.Paths.arc('a', '0 0,0',
|
||||
BRC.CORNER_RADIUS,
|
||||
Blockly.utils.Paths.point(
|
||||
-BRC.DISTANCE_45_OUTSIDE - BRC.HIGHLIGHT_OFFSET,
|
||||
BRC.CORNER_RADIUS - BRC.DISTANCE_45_OUTSIDE));
|
||||
|
||||
/**
|
||||
* SVG path for drawing highlight on the bottom-left corner of a statement
|
||||
* input in RTL.
|
||||
* @const
|
||||
*/
|
||||
BRC.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_RTL =
|
||||
Blockly.utils.Paths.arc('a', '0 0,0',
|
||||
BRC.CORNER_RADIUS + BRC.HIGHLIGHT_OFFSET,
|
||||
Blockly.utils.Paths.point(
|
||||
BRC.CORNER_RADIUS + BRC.HIGHLIGHT_OFFSET,
|
||||
BRC.CORNER_RADIUS + BRC.HIGHLIGHT_OFFSET));
|
||||
|
||||
/**
|
||||
* SVG path for drawing highlight on the bottom-left corner of a statement
|
||||
* input in LTR.
|
||||
* @const
|
||||
*/
|
||||
BRC.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_LTR =
|
||||
Blockly.utils.Paths.arc('a', '0 0,0',
|
||||
BRC.CORNER_RADIUS + BRC.HIGHLIGHT_OFFSET,
|
||||
Blockly.utils.Paths.point(
|
||||
BRC.CORNER_RADIUS - BRC.DISTANCE_45_OUTSIDE,
|
||||
BRC.DISTANCE_45_OUTSIDE + BRC.HIGHLIGHT_OFFSET));
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SVG start point for drawing the top-left corner.
|
||||
* @const
|
||||
*/
|
||||
BRC.TOP_LEFT_CORNER_START =
|
||||
'm 0,' + BRC.CORNER_RADIUS;
|
||||
|
||||
/**
|
||||
* SVG path for drawing the rounded top-left corner.
|
||||
* @const
|
||||
*/
|
||||
BRC.TOP_LEFT_CORNER =
|
||||
'A ' + BRC.CORNER_RADIUS + ',' +
|
||||
BRC.CORNER_RADIUS + ' 0 0,1 ' +
|
||||
BRC.CORNER_RADIUS + ',0';
|
||||
|
||||
BRC.BOTTOM_LEFT_CORNER = 'a' + BRC.CORNER_RADIUS + ',' +
|
||||
BRC.CORNER_RADIUS + ' 0 0,1 -' +
|
||||
BRC.CORNER_RADIUS + ',-' +
|
||||
BRC.CORNER_RADIUS;
|
||||
|
||||
BRC.BOTTOM_LEFT_CORNER_HIGHLIGHT_START =
|
||||
'M ' + BRC.DISTANCE_45_INSIDE + ', '; // follow with y pos - distance 45 inside
|
||||
|
||||
BRC.BOTTOM_LEFT_CORNER_HIGHLIGHT_MID =
|
||||
'A ' + (BRC.CORNER_RADIUS - BRC.HIGHLIGHT_OFFSET) +
|
||||
',' + (BRC.CORNER_RADIUS - BRC.HIGHLIGHT_OFFSET) +
|
||||
' 0 0,1 ' + BRC.HIGHLIGHT_OFFSET + ','; // follow with y pos - corner radius
|
||||
|
||||
BRC.OUTPUT_CONNECTION_HIGHLIGHT_LTR =
|
||||
'V ' + (BRC.TAB_HEIGHT + BRC.TAB_OFFSET_FROM_TOP - 1.5) +
|
||||
' m ' + (BRC.TAB_WIDTH * -0.92) + ',-0.5 ' +
|
||||
'q ' + (BRC.TAB_WIDTH * -0.19) + ',-5.5 0,-11 ' +
|
||||
'm ' + (BRC.TAB_WIDTH * 0.92) + ',1 ' +
|
||||
'V 0.5 H 1';
|
||||
|
||||
BRC.OUTPUT_CONNECTION_HIGHLIGHT_RTL =
|
||||
'M ' + (BRC.TAB_WIDTH * -0.25) + ',8.4 l ' +
|
||||
(BRC.TAB_WIDTH * -0.45) + ',-2.1';
|
||||
|
||||
/**
|
||||
* SVG start point for drawing the top-left corner's highlight in RTL.
|
||||
* @const
|
||||
*/
|
||||
BRC.TOP_LEFT_CORNER_START_HIGHLIGHT_RTL =
|
||||
'm ' + BRC.DISTANCE_45_INSIDE + ',' +
|
||||
BRC.DISTANCE_45_INSIDE;
|
||||
|
||||
/**
|
||||
* SVG start point for drawing the top-left corner's highlight in LTR.
|
||||
* @const
|
||||
*/
|
||||
BRC.TOP_LEFT_CORNER_START_HIGHLIGHT_LTR =
|
||||
'm 0.5,' + (BRC.CORNER_RADIUS - 0.5);
|
||||
|
||||
/**
|
||||
* SVG path for drawing the highlight on the rounded top-left corner.
|
||||
* @const
|
||||
*/
|
||||
BRC.TOP_LEFT_CORNER_HIGHLIGHT =
|
||||
'A ' + (BRC.CORNER_RADIUS - 0.5) + ',' +
|
||||
(BRC.CORNER_RADIUS - 0.5) + ' 0 0,1 ' +
|
||||
BRC.CORNER_RADIUS + ',0.5';
|
||||
|
||||
/**
|
||||
* Path of the top hat's curve's highlight in LTR.
|
||||
* @const
|
||||
*/
|
||||
BRC.START_HAT_HIGHLIGHT_LTR =
|
||||
Blockly.utils.Paths.curve('c',
|
||||
[
|
||||
Blockly.utils.Paths.point(17.8, -9.2),
|
||||
Blockly.utils.Paths.point(45.3, -14.9),
|
||||
Blockly.utils.Paths.point(75, -8.7)
|
||||
]) +
|
||||
Blockly.utils.Paths.moveTo(100.5, 0.5);
|
||||
|
||||
/**
|
||||
* Path of the top hat's curve's highlight in RTL.
|
||||
* @const
|
||||
*/
|
||||
BRC.START_HAT_HIGHLIGHT_RTL =
|
||||
Blockly.utils.Paths.moveBy(25, -8.7) +
|
||||
Blockly.utils.Paths.curve('c',
|
||||
[
|
||||
Blockly.utils.Paths.point(29.7, -6.2),
|
||||
Blockly.utils.Paths.point(57.2, -0.5),
|
||||
Blockly.utils.Paths.point(75, 8.7)
|
||||
]);
|
||||
|
||||
484
core/renderers/block_rendering_rewrite/measurables.js
Normal file
484
core/renderers/block_rendering_rewrite/measurables.js
Normal file
@@ -0,0 +1,484 @@
|
||||
goog.provide('Blockly.BlockRendering.Measurable');
|
||||
|
||||
goog.require('BRC');
|
||||
/* global BRC */
|
||||
|
||||
/**
|
||||
* The base class to represent a part of a block that takes up space during
|
||||
* rendering. The constructor for each non-spacer Measurable records the size
|
||||
* of the block element (e.g. field, statement input).
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable = function() {
|
||||
this.isInput = false;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
this.type = null;
|
||||
|
||||
this.xPos = 0;
|
||||
this.centerline = 0;
|
||||
};
|
||||
|
||||
// TODO: We may remove these helper functions if all of them end up being direct
|
||||
// checks against types.
|
||||
|
||||
/**
|
||||
* Whether this stores information about a field.
|
||||
* @return {boolean} True if this object stores information about a field.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isField = function() {
|
||||
return this.type == 'field';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about a hat.
|
||||
* @return {boolean} True if this object stores information about a hat.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isHat = function() {
|
||||
return this.type == 'hat';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about an icon.
|
||||
* @return {boolean} True if this object stores information about an icon.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isIcon = function() {
|
||||
return this.type == 'icon';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about a spacer.
|
||||
* @return {boolean} True if this object stores information about a spacer.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isSpacer = function() {
|
||||
return this.type == 'between-row spacer' || this.type == 'in-row spacer';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about an external input.
|
||||
* @return {boolean} True if this object stores information about an external
|
||||
* input.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isExternalInput = function() {
|
||||
return this.type == 'external value input';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about a inline input.
|
||||
* @return {boolean} True if this object stores information about a inline
|
||||
* input.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isInlineInput = function() {
|
||||
return this.type == 'inline input';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about a statement input.
|
||||
* @return {boolean} True if this object stores information about a statement
|
||||
* input.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isStatementInput = function() {
|
||||
return this.type == 'statement input';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about a previous connection.
|
||||
* @return {boolean} True if this object stores information about a previous
|
||||
* connection.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isPreviousConnection = function() {
|
||||
return this.type == 'previous connection';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about a next connection.
|
||||
* @return {boolean} True if this object stores information about an next
|
||||
* connection.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isNextConnection = function() {
|
||||
return this.type == 'next connection';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about a rounded corner.
|
||||
* @return {boolean} True if this object stores information about an rounded
|
||||
* corner.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isRoundedCorner = function() {
|
||||
return this.type == 'round corner';
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this stores information about a square corner.
|
||||
* @return {boolean} True if this object stores information about an square
|
||||
* corner.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.Measurable.prototype.isSquareCorner = function() {
|
||||
return this.type == 'square corner';
|
||||
};
|
||||
/**
|
||||
* The base class to represent an input that takes up space on a block
|
||||
* during rendering
|
||||
* @param {!Blockly.Input} input The input to measure and store information for.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.Input = function(input) {
|
||||
Blockly.BlockRendering.Input.superClass_.constructor.call(this);
|
||||
|
||||
this.isInput = true;
|
||||
this.input = input;
|
||||
this.align = input.align;
|
||||
this.connectedBlock = input.connection && input.connection.targetBlock() ?
|
||||
input.connection.targetBlock() : null;
|
||||
|
||||
if (this.connectedBlock) {
|
||||
var bBox = this.connectedBlock.getHeightWidth();
|
||||
this.connectedBlockWidth = bBox.width;
|
||||
this.connectedBlockHeight = bBox.height;
|
||||
} else {
|
||||
this.connectedBlockWidth = 0;
|
||||
this.connectedBlockHeight = 0;
|
||||
}
|
||||
|
||||
this.connection = input.connection;
|
||||
this.connectionOffsetX = 0;
|
||||
this.connectionOffsetY = 0;
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.Input, Blockly.BlockRendering.Measurable);
|
||||
|
||||
|
||||
/**
|
||||
* An object containing information about the space an icon takes up during
|
||||
* rendering
|
||||
* @param {!Blockly.Icon} icon The icon to measure and store information for.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.Icon = function(icon) {
|
||||
Blockly.BlockRendering.Icon.superClass_.constructor.call(this);
|
||||
this.icon = icon;
|
||||
this.isVisible = icon.isVisible();
|
||||
this.type = 'icon';
|
||||
|
||||
var size = icon.getCorrectedSize();
|
||||
this.height = size.height;
|
||||
this.width = size.width;
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.Icon, Blockly.BlockRendering.Measurable);
|
||||
|
||||
/**
|
||||
* An object containing information about the space a field takes up during
|
||||
* rendering
|
||||
* @param {!Blockly.Field} field The field to measure and store information for.
|
||||
* @param {!Blockly.Input} parentInput The parent input for the field.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.Field = function(field, parentInput) {
|
||||
Blockly.BlockRendering.Field.superClass_.constructor.call(this);
|
||||
this.field = field;
|
||||
this.isEditable = field.isCurrentlyEditable();
|
||||
this.type = 'field';
|
||||
|
||||
var size = this.field.getCorrectedSize();
|
||||
this.height = size.height;
|
||||
this.width = size.width;
|
||||
this.parentInput = parentInput;
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.Field, Blockly.BlockRendering.Measurable);
|
||||
|
||||
/**
|
||||
* An object containing information about the space an inline input takes up
|
||||
* during rendering
|
||||
* @param {!Blockly.Input} input The inline input to measure and store
|
||||
* information for.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.InlineInput = function(input) {
|
||||
Blockly.BlockRendering.InlineInput.superClass_.constructor.call(this, input);
|
||||
this.type = 'inline input';
|
||||
|
||||
if (!this.connectedBlock) {
|
||||
this.height = BRC.EMPTY_INLINE_INPUT_HEIGHT;
|
||||
this.width = BRC.EMPTY_INLINE_INPUT_WIDTH;
|
||||
} else {
|
||||
// We allow the dark path to show on the parent block so that the child
|
||||
// block looks embossed. This takes up an extra pixel in both x and y.
|
||||
this.width = this.connectedBlockWidth + BRC.TAB_WIDTH + BRC.DARK_PATH_OFFSET;
|
||||
this.height = this.connectedBlockHeight + BRC.DARK_PATH_OFFSET;
|
||||
}
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.InlineInput, Blockly.BlockRendering.Input);
|
||||
|
||||
/**
|
||||
* An object containing information about the space a statement input takes up
|
||||
* during rendering
|
||||
* @param {!Blockly.Input} input The statement input to measure and store
|
||||
* information for.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.StatementInput = function(input) {
|
||||
Blockly.BlockRendering.StatementInput.superClass_.constructor.call(this, input);
|
||||
this.type = 'statement input';
|
||||
|
||||
if (!this.connectedBlock) {
|
||||
this.height = BRC.EMPTY_STATEMENT_INPUT_HEIGHT;
|
||||
this.width = BRC.EMPTY_STATEMENT_INPUT_WIDTH;
|
||||
} else {
|
||||
this.width = BRC.POPULATED_STATEMENT_INPUT_WIDTH;
|
||||
this.height = this.connectedBlockHeight + BRC.STATEMENT_BOTTOM_SPACER;
|
||||
if (this.connectedBlock.nextConnection) {
|
||||
this.height -= BRC.NOTCH_HEIGHT;
|
||||
}
|
||||
}
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.StatementInput,
|
||||
Blockly.BlockRendering.Input);
|
||||
|
||||
/**
|
||||
* An object containing information about the space an external value input
|
||||
* takes up during rendering
|
||||
* @param {!Blockly.Input} input The external value input to measure and store
|
||||
* information for.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.ExternalValueInput = function(input) {
|
||||
Blockly.BlockRendering.ExternalValueInput.superClass_.constructor.call(this, input);
|
||||
this.type = 'external value input';
|
||||
|
||||
if (!this.connectedBlock) {
|
||||
this.height = BRC.TAB_HEIGHT;
|
||||
} else {
|
||||
this.height = this.connectedBlockHeight - 2 * BRC.TAB_OFFSET_FROM_TOP;
|
||||
}
|
||||
this.width = BRC.EXTERNAL_VALUE_INPUT_WIDTH;
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.ExternalValueInput,
|
||||
Blockly.BlockRendering.Input);
|
||||
|
||||
/**
|
||||
* An object containing information about the space a previous connection takes
|
||||
* up during rendering.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.PreviousConnection = function() {
|
||||
Blockly.BlockRendering.PreviousConnection.superClass_.constructor.call(this);
|
||||
this.type = 'previous connection';
|
||||
this.height = BRC.NOTCH_HEIGHT;
|
||||
this.width = BRC.NOTCH_WIDTH;
|
||||
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.PreviousConnection, Blockly.BlockRendering.Measurable);
|
||||
|
||||
/**
|
||||
* An object containing information about the space a next connection takes
|
||||
* up during rendering.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.NextConnection = function() {
|
||||
Blockly.BlockRendering.NextConnection.superClass_.constructor.call(this);
|
||||
this.type = 'next connection';
|
||||
this.height = BRC.NOTCH_HEIGHT;
|
||||
this.width = BRC.NOTCH_WIDTH;
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.NextConnection, Blockly.BlockRendering.Measurable);
|
||||
|
||||
/**
|
||||
* An object containing information about the space a hat takes up during
|
||||
* rendering.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.Hat = function() {
|
||||
Blockly.BlockRendering.Hat.superClass_.constructor.call(this);
|
||||
this.type = 'hat';
|
||||
this.height = BRC.NO_PADDING;
|
||||
this.width = BRC.START_HAT_WIDTH;
|
||||
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.Hat, Blockly.BlockRendering.Measurable);
|
||||
|
||||
/**
|
||||
* An object containing information about the space a square corner takes up
|
||||
* during rendering.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.SquareCorner = function() {
|
||||
Blockly.BlockRendering.SquareCorner.superClass_.constructor.call(this);
|
||||
this.type = 'square corner';
|
||||
this.height = BRC.NOTCH_HEIGHT;
|
||||
this.width = BRC.NO_PADDING;
|
||||
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.SquareCorner, Blockly.BlockRendering.Measurable);
|
||||
|
||||
/**
|
||||
* An object containing information about the space a rounded corner takes up
|
||||
* during rendering.
|
||||
* @package
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockRendering.RoundCorner = function() {
|
||||
Blockly.BlockRendering.RoundCorner.superClass_.constructor.call(this);
|
||||
this.type = 'round corner';
|
||||
this.width = BRC.CORNER_RADIUS;
|
||||
// The rounded corner extends into the next row by 4 so we only take the
|
||||
// height that is aligned with this row.
|
||||
this.height = BRC.NOTCH_HEIGHT;
|
||||
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.RoundCorner, Blockly.BlockRendering.Measurable);
|
||||
|
||||
|
||||
Blockly.BlockRendering.Row = function() {
|
||||
this.type = 'row';
|
||||
this.yPos = 0;
|
||||
this.elements = [];
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
|
||||
this.hasExternalInput = false;
|
||||
this.hasStatement = false;
|
||||
this.hasInlineInput = false;
|
||||
this.hasDummyInput = false;
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Row.prototype.isSpacer = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Row.prototype.measure = function() {
|
||||
var connectedBlockWidths = 0;
|
||||
for (var e = 0; e < this.elements.length; e++) {
|
||||
var elem = this.elements[e];
|
||||
this.width += elem.width;
|
||||
if (elem.isInput &&
|
||||
(elem.type == 'statement input' || elem.type == 'external value input')) {
|
||||
connectedBlockWidths += elem.connectedBlockWidth;
|
||||
}
|
||||
if (!(elem.isSpacer())) {
|
||||
this.height = Math.max(this.height, elem.height);
|
||||
}
|
||||
}
|
||||
this.widthWithConnectedBlocks = this.width + connectedBlockWidths;
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Row.prototype.getLastInput = function() {
|
||||
// There's always a spacer after the last input, unless there are no inputs.
|
||||
if (this.elements.length > 1) {
|
||||
var elem = this.elements[this.elements.length - 2];
|
||||
if (elem.isInput) {
|
||||
return elem;
|
||||
} else if (elem.isField()) {
|
||||
return elem.parentInput;
|
||||
}
|
||||
}
|
||||
// Return null if there are no inputs.
|
||||
return null;
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Row.prototype.getFirstSpacer = function() {
|
||||
return this.elements[0];
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.Row.prototype.getLastSpacer = function() {
|
||||
return this.elements[this.elements.length - 1];
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.BetweenRowSpacer = function(height, width) {
|
||||
this.type = 'between-row spacer';
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.followsStatement = false;
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.BetweenRowSpacer,
|
||||
Blockly.BlockRendering.Measurable);
|
||||
|
||||
Blockly.BlockRendering.InRowSpacer = function(width) {
|
||||
this.type = 'in-row spacer';
|
||||
this.width = width;
|
||||
this.height = BRC.SPACER_DEFAULT_HEIGHT;
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.InRowSpacer,
|
||||
Blockly.BlockRendering.Measurable);
|
||||
|
||||
/**
|
||||
* An object containing information about what elements are in the top row of a
|
||||
* block as well as spacing information for the top row.
|
||||
* Elements in a top row can consist of corners, hats and previous connections.
|
||||
* @param {[type]} block [description]
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockRendering.TopRow = function(block) {
|
||||
Blockly.BlockRendering.TopRow.superClass_.constructor.call(this);
|
||||
|
||||
this.elements = [];
|
||||
this.type = 'top row';
|
||||
|
||||
this.hasPreviousConnection = !!block.previousConnection;
|
||||
this.connection = block.previousConnection;
|
||||
|
||||
var precedesStatement = block.inputList.length &&
|
||||
block.inputList[0].type == Blockly.NEXT_STATEMENT;
|
||||
|
||||
// This is the minimum height for the row. If one of its elements has a greater
|
||||
// height it will be overwritten in the compute pass.
|
||||
if (precedesStatement) {
|
||||
this.height = BRC.LARGE_PADDING;
|
||||
} else {
|
||||
this.height = BRC.MEDIUM_PADDING;
|
||||
}
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.TopRow, Blockly.BlockRendering.Row);
|
||||
|
||||
|
||||
Blockly.BlockRendering.TopRow.prototype.isSpacer = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
Blockly.BlockRendering.BottomRow = function(block) {
|
||||
Blockly.BlockRendering.BottomRow.superClass_.constructor.call(this);
|
||||
this.type = 'bottom row';
|
||||
this.hasNextConnection = !!block.nextConnection;
|
||||
this.connection = block.nextConnection;
|
||||
|
||||
var followsStatement =
|
||||
block.inputList.length &&
|
||||
block.inputList[block.inputList.length - 1].type == Blockly.NEXT_STATEMENT;
|
||||
this.hasFixedWidth = followsStatement && block.getInputsInline();
|
||||
|
||||
// This is the minimum height for the row. If one of it's elements has a greater
|
||||
// height it will be overwritten in the compute pass.
|
||||
if (followsStatement) {
|
||||
this.height = BRC.LARGE_PADDING;
|
||||
} else {
|
||||
this.height = BRC.NOTCH_HEIGHT;
|
||||
}
|
||||
|
||||
};
|
||||
goog.inherits(Blockly.BlockRendering.BottomRow,
|
||||
Blockly.BlockRendering.Row);
|
||||
|
||||
Blockly.BlockRendering.BottomRow.prototype.isSpacer = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user