123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969 |
- /**
- * @license
- * Visual Blocks Editor
- *
- * Copyright 2016 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.BlockSvg.render');
- goog.require('Blockly.BlockSvg');
- // UI constants for rendering blocks.
- /**
- * Horizontal space between elements.
- * @const
- */
- Blockly.BlockSvg.SEP_SPACE_X = 10;
- /**
- * Vertical space between elements.
- * @const
- */
- Blockly.BlockSvg.SEP_SPACE_Y = 10;
- /**
- * Vertical padding around inline elements.
- * @const
- */
- Blockly.BlockSvg.INLINE_PADDING_Y = 5;
- /**
- * Minimum height of a block.
- * @const
- */
- Blockly.BlockSvg.MIN_BLOCK_Y = 25;
- /**
- * Height of horizontal puzzle tab.
- * @const
- */
- Blockly.BlockSvg.TAB_HEIGHT = 20;
- /**
- * Width of horizontal puzzle tab.
- * @const
- */
- Blockly.BlockSvg.TAB_WIDTH = 8;
- /**
- * Width of vertical tab (inc left margin).
- * @const
- */
- Blockly.BlockSvg.NOTCH_WIDTH = 30;
- /**
- * Rounded corner radius.
- * @const
- */
- Blockly.BlockSvg.CORNER_RADIUS = 8;
- /**
- * Do blocks with no previous or output connections have a 'hat' on top?
- * @const
- */
- Blockly.BlockSvg.START_HAT = false;
- /**
- * Height of the top hat.
- * @const
- */
- Blockly.BlockSvg.START_HAT_HEIGHT = 15;
- /**
- * Path of the top hat's curve.
- * @const
- */
- Blockly.BlockSvg.START_HAT_PATH = 'c 30,-' +
- Blockly.BlockSvg.START_HAT_HEIGHT + ' 70,-' +
- Blockly.BlockSvg.START_HAT_HEIGHT + ' 100,0';
- /**
- * Path of the top hat's curve's highlight in LTR.
- * @const
- */
- Blockly.BlockSvg.START_HAT_HIGHLIGHT_LTR =
- 'c 17.8,-9.2 45.3,-14.9 75,-8.7 M 100.5,0.5';
- /**
- * Path of the top hat's curve's highlight in RTL.
- * @const
- */
- Blockly.BlockSvg.START_HAT_HIGHLIGHT_RTL =
- 'm 25,-8.7 c 29.7,-6.2 57.2,-0.5 75,8.7';
- /**
- * Distance from shape edge to intersect with a curved corner at 45 degrees.
- * Applies to highlighting on around the inside of a curve.
- * @const
- */
- Blockly.BlockSvg.DISTANCE_45_INSIDE = (1 - Math.SQRT1_2) *
- (Blockly.BlockSvg.CORNER_RADIUS - 0.5) + 0.5;
- /**
- * Distance from shape edge to intersect with a curved corner at 45 degrees.
- * Applies to highlighting on around the outside of a curve.
- * @const
- */
- Blockly.BlockSvg.DISTANCE_45_OUTSIDE = (1 - Math.SQRT1_2) *
- (Blockly.BlockSvg.CORNER_RADIUS + 0.5) - 0.5;
- /**
- * SVG path for drawing next/previous notch from left to right.
- * @const
- */
- Blockly.BlockSvg.NOTCH_PATH_LEFT = 'l 6,4 3,0 6,-4';
- /**
- * SVG path for drawing next/previous notch from left to right with
- * highlighting.
- * @const
- */
- Blockly.BlockSvg.NOTCH_PATH_LEFT_HIGHLIGHT = 'l 6,4 3,0 6,-4';
- /**
- * SVG path for drawing next/previous notch from right to left.
- * @const
- */
- Blockly.BlockSvg.NOTCH_PATH_RIGHT = 'l -6,4 -3,0 -6,-4';
- /**
- * SVG path for drawing jagged teeth at the end of collapsed blocks.
- * @const
- */
- Blockly.BlockSvg.JAGGED_TEETH = 'l 8,0 0,4 8,4 -16,8 8,4';
- /**
- * Height of SVG path for jagged teeth at the end of collapsed blocks.
- * @const
- */
- Blockly.BlockSvg.JAGGED_TEETH_HEIGHT = 20;
- /**
- * Width of SVG path for jagged teeth at the end of collapsed blocks.
- * @const
- */
- Blockly.BlockSvg.JAGGED_TEETH_WIDTH = 15;
- /**
- * SVG path for drawing a horizontal puzzle tab from top to bottom.
- * @const
- */
- Blockly.BlockSvg.TAB_PATH_DOWN = 'v 5 c 0,10 -' + Blockly.BlockSvg.TAB_WIDTH +
- ',-8 -' + Blockly.BlockSvg.TAB_WIDTH + ',7.5 s ' +
- Blockly.BlockSvg.TAB_WIDTH + ',-2.5 ' + Blockly.BlockSvg.TAB_WIDTH + ',7.5';
- /**
- * SVG path for drawing a horizontal puzzle tab from top to bottom with
- * highlighting from the upper-right.
- * @const
- */
- Blockly.BlockSvg.TAB_PATH_DOWN_HIGHLIGHT_RTL = 'v 6.5 m -' +
- (Blockly.BlockSvg.TAB_WIDTH * 0.97) + ',3 q -' +
- (Blockly.BlockSvg.TAB_WIDTH * 0.05) + ',10 ' +
- (Blockly.BlockSvg.TAB_WIDTH * 0.3) + ',9.5 m ' +
- (Blockly.BlockSvg.TAB_WIDTH * 0.67) + ',-1.9 v 1.4';
- /**
- * SVG start point for drawing the top-left corner.
- * @const
- */
- Blockly.BlockSvg.TOP_LEFT_CORNER_START =
- 'm 0,' + Blockly.BlockSvg.CORNER_RADIUS;
- /**
- * SVG start point for drawing the top-left corner's highlight in RTL.
- * @const
- */
- Blockly.BlockSvg.TOP_LEFT_CORNER_START_HIGHLIGHT_RTL =
- 'm ' + Blockly.BlockSvg.DISTANCE_45_INSIDE + ',' +
- Blockly.BlockSvg.DISTANCE_45_INSIDE;
- /**
- * SVG start point for drawing the top-left corner's highlight in LTR.
- * @const
- */
- Blockly.BlockSvg.TOP_LEFT_CORNER_START_HIGHLIGHT_LTR =
- 'm 0.5,' + (Blockly.BlockSvg.CORNER_RADIUS - 0.5);
- /**
- * SVG path for drawing the rounded top-left corner.
- * @const
- */
- Blockly.BlockSvg.TOP_LEFT_CORNER =
- 'A ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
- Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' +
- Blockly.BlockSvg.CORNER_RADIUS + ',0';
- /**
- * SVG path for drawing the highlight on the rounded top-left corner.
- * @const
- */
- Blockly.BlockSvg.TOP_LEFT_CORNER_HIGHLIGHT =
- 'A ' + (Blockly.BlockSvg.CORNER_RADIUS - 0.5) + ',' +
- (Blockly.BlockSvg.CORNER_RADIUS - 0.5) + ' 0 0,1 ' +
- Blockly.BlockSvg.CORNER_RADIUS + ',0.5';
- /**
- * 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
- */
- Blockly.BlockSvg.INNER_TOP_LEFT_CORNER =
- Blockly.BlockSvg.NOTCH_PATH_RIGHT + ' h -' +
- (Blockly.BlockSvg.NOTCH_WIDTH - 15 - Blockly.BlockSvg.CORNER_RADIUS) +
- ' a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
- Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 -' +
- Blockly.BlockSvg.CORNER_RADIUS + ',' +
- Blockly.BlockSvg.CORNER_RADIUS;
- /**
- * SVG path for drawing the bottom-left corner of a statement input.
- * Includes the rounded inside corner.
- * @const
- */
- Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER =
- 'a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
- Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
- Blockly.BlockSvg.CORNER_RADIUS + ',' +
- Blockly.BlockSvg.CORNER_RADIUS;
- /**
- * SVG path for drawing highlight on the top-left corner of a statement
- * input in RTL.
- * @const
- */
- Blockly.BlockSvg.INNER_TOP_LEFT_CORNER_HIGHLIGHT_RTL =
- 'a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' +
- Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' +
- (-Blockly.BlockSvg.DISTANCE_45_OUTSIDE - 0.5) + ',' +
- (Blockly.BlockSvg.CORNER_RADIUS -
- Blockly.BlockSvg.DISTANCE_45_OUTSIDE);
- /**
- * SVG path for drawing highlight on the bottom-left corner of a statement
- * input in RTL.
- * @const
- */
- Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_RTL =
- 'a ' + (Blockly.BlockSvg.CORNER_RADIUS + 0.5) + ',' +
- (Blockly.BlockSvg.CORNER_RADIUS + 0.5) + ' 0 0,0 ' +
- (Blockly.BlockSvg.CORNER_RADIUS + 0.5) + ',' +
- (Blockly.BlockSvg.CORNER_RADIUS + 0.5);
- /**
- * SVG path for drawing highlight on the bottom-left corner of a statement
- * input in LTR.
- * @const
- */
- Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_LTR =
- 'a ' + (Blockly.BlockSvg.CORNER_RADIUS + 0.5) + ',' +
- (Blockly.BlockSvg.CORNER_RADIUS + 0.5) + ' 0 0,0 ' +
- (Blockly.BlockSvg.CORNER_RADIUS -
- Blockly.BlockSvg.DISTANCE_45_OUTSIDE) + ',' +
- (Blockly.BlockSvg.DISTANCE_45_OUTSIDE + 0.5);
- /**
- * Render the block.
- * Lays out and reflows a block based on its contents and settings.
- * @param {boolean=} opt_bubble If false, just render this block.
- * If true, also render block's parent, grandparent, etc. Defaults to true.
- */
- Blockly.BlockSvg.prototype.render = function(opt_bubble) {
- Blockly.Field.startCache();
- this.rendered = true;
- var cursorX = Blockly.BlockSvg.SEP_SPACE_X;
- if (this.RTL) {
- cursorX = -cursorX;
- }
- // Move the icons into position.
- var icons = this.getIcons();
- for (var i = 0; i < icons.length; i++) {
- cursorX = icons[i].renderIcon(cursorX);
- }
- cursorX += this.RTL ?
- Blockly.BlockSvg.SEP_SPACE_X : -Blockly.BlockSvg.SEP_SPACE_X;
- // If there are no icons, cursorX will be 0, otherwise it will be the
- // width that the first label needs to move over by.
- var inputRows = this.renderCompute_(cursorX);
- this.renderDraw_(cursorX, inputRows);
- this.renderMoveConnections_();
- if (opt_bubble !== false) {
- // Render all blocks above this one (propagate a reflow).
- var parentBlock = this.getParent();
- if (parentBlock) {
- parentBlock.render(true);
- } else {
- // Top-most block. Fire an event to allow scrollbars to resize.
- this.workspace.resizeContents();
- }
- }
- Blockly.Field.stopCache();
- };
- /**
- * Render a list of fields starting at the specified location.
- * @param {!Array.<!Blockly.Field>} fieldList List of fields.
- * @param {number} cursorX X-coordinate to start the fields.
- * @param {number} cursorY Y-coordinate to start the fields.
- * @return {number} X-coordinate of the end of the field row (plus a gap).
- * @private
- */
- Blockly.BlockSvg.prototype.renderFields_ =
- function(fieldList, cursorX, cursorY) {
- /* eslint-disable indent */
- cursorY += Blockly.BlockSvg.INLINE_PADDING_Y;
- if (this.RTL) {
- cursorX = -cursorX;
- }
- for (var t = 0, field; field = fieldList[t]; t++) {
- var root = field.getSvgRoot();
- if (!root) {
- continue;
- }
- if (this.RTL) {
- cursorX -= field.renderSep + field.renderWidth;
- root.setAttribute('transform',
- 'translate(' + cursorX + ',' + cursorY + ')');
- if (field.renderWidth) {
- cursorX -= Blockly.BlockSvg.SEP_SPACE_X;
- }
- } else {
- root.setAttribute('transform',
- 'translate(' + (cursorX + field.renderSep) + ',' + cursorY + ')');
- if (field.renderWidth) {
- cursorX += field.renderSep + field.renderWidth +
- Blockly.BlockSvg.SEP_SPACE_X;
- }
- }
- }
- return this.RTL ? -cursorX : cursorX;
- }; /* eslint-enable indent */
- /**
- * Computes the height and widths for each row and field.
- * @param {number} iconWidth Offset of first row due to icons.
- * @return {!Array.<!Array.<!Object>>} 2D array of objects, each containing
- * position information.
- * @private
- */
- Blockly.BlockSvg.prototype.renderCompute_ = function(iconWidth) {
- var inputList = this.inputList;
- var inputRows = [];
- inputRows.rightEdge = iconWidth + Blockly.BlockSvg.SEP_SPACE_X * 2;
- if (this.previousConnection || this.nextConnection) {
- inputRows.rightEdge = Math.max(inputRows.rightEdge,
- Blockly.BlockSvg.NOTCH_WIDTH + Blockly.BlockSvg.SEP_SPACE_X);
- }
- var fieldValueWidth = 0; // Width of longest external value field.
- var fieldStatementWidth = 0; // Width of longest statement field.
- var hasValue = false;
- var hasStatement = false;
- var hasDummy = false;
- var lastType = undefined;
- var isInline = this.getInputsInline() && !this.isCollapsed();
- for (var i = 0, input; input = inputList[i]; i++) {
- if (!input.isVisible()) {
- continue;
- }
- var row;
- if (!isInline || !lastType ||
- lastType == Blockly.NEXT_STATEMENT ||
- input.type == Blockly.NEXT_STATEMENT) {
- // Create new row.
- lastType = input.type;
- row = [];
- if (isInline && input.type != Blockly.NEXT_STATEMENT) {
- row.type = Blockly.BlockSvg.INLINE;
- } else {
- row.type = input.type;
- }
- row.height = 0;
- inputRows.push(row);
- } else {
- row = inputRows[inputRows.length - 1];
- }
- row.push(input);
- // Compute minimum input size.
- input.renderHeight = Blockly.BlockSvg.MIN_BLOCK_Y;
- // The width is currently only needed for inline value inputs.
- if (isInline && input.type == Blockly.INPUT_VALUE) {
- input.renderWidth = Blockly.BlockSvg.TAB_WIDTH +
- Blockly.BlockSvg.SEP_SPACE_X * 1.25;
- } else {
- input.renderWidth = 0;
- }
- // Expand input size if there is a connection.
- if (input.connection && input.connection.isConnected()) {
- var linkedBlock = input.connection.targetBlock();
- var bBox = linkedBlock.getHeightWidth();
- input.renderHeight = Math.max(input.renderHeight, bBox.height);
- input.renderWidth = Math.max(input.renderWidth, bBox.width);
- }
- // Blocks have a one pixel shadow that should sometimes overhang.
- if (!isInline && i == inputList.length - 1) {
- // Last value input should overhang.
- input.renderHeight--;
- } else if (!isInline && input.type == Blockly.INPUT_VALUE &&
- inputList[i + 1] && inputList[i + 1].type == Blockly.NEXT_STATEMENT) {
- // Value input above statement input should overhang.
- input.renderHeight--;
- }
- row.height = Math.max(row.height, input.renderHeight);
- input.fieldWidth = 0;
- if (inputRows.length == 1) {
- // The first row gets shifted to accommodate any icons.
- input.fieldWidth += this.RTL ? -iconWidth : iconWidth;
- }
- var previousFieldEditable = false;
- for (var j = 0, field; field = input.fieldRow[j]; j++) {
- if (j != 0) {
- input.fieldWidth += Blockly.BlockSvg.SEP_SPACE_X;
- }
- // Get the dimensions of the field.
- var fieldSize = field.getSize();
- field.renderWidth = fieldSize.width;
- field.renderSep = (previousFieldEditable && field.EDITABLE) ?
- Blockly.BlockSvg.SEP_SPACE_X : 0;
- input.fieldWidth += field.renderWidth + field.renderSep;
- row.height = Math.max(row.height, fieldSize.height);
- previousFieldEditable = field.EDITABLE;
- }
- if (row.type != Blockly.BlockSvg.INLINE) {
- if (row.type == Blockly.NEXT_STATEMENT) {
- hasStatement = true;
- fieldStatementWidth = Math.max(fieldStatementWidth, input.fieldWidth);
- } else {
- if (row.type == Blockly.INPUT_VALUE) {
- hasValue = true;
- } else if (row.type == Blockly.DUMMY_INPUT) {
- hasDummy = true;
- }
- fieldValueWidth = Math.max(fieldValueWidth, input.fieldWidth);
- }
- }
- }
- // Make inline rows a bit thicker in order to enclose the values.
- for (var y = 0, row; row = inputRows[y]; y++) {
- row.thicker = false;
- if (row.type == Blockly.BlockSvg.INLINE) {
- for (var z = 0, input; input = row[z]; z++) {
- if (input.type == Blockly.INPUT_VALUE) {
- row.height += 2 * Blockly.BlockSvg.INLINE_PADDING_Y;
- row.thicker = true;
- break;
- }
- }
- }
- }
- // Compute the statement edge.
- // This is the width of a block where statements are nested.
- inputRows.statementEdge = 2 * Blockly.BlockSvg.SEP_SPACE_X +
- fieldStatementWidth;
- // Compute the preferred right edge. Inline blocks may extend beyond.
- // This is the width of the block where external inputs connect.
- if (hasStatement) {
- inputRows.rightEdge = Math.max(inputRows.rightEdge,
- inputRows.statementEdge + Blockly.BlockSvg.NOTCH_WIDTH);
- }
- if (hasValue) {
- inputRows.rightEdge = Math.max(inputRows.rightEdge, fieldValueWidth +
- Blockly.BlockSvg.SEP_SPACE_X * 2 + Blockly.BlockSvg.TAB_WIDTH);
- } else if (hasDummy) {
- inputRows.rightEdge = Math.max(inputRows.rightEdge, fieldValueWidth +
- Blockly.BlockSvg.SEP_SPACE_X * 2);
- }
- inputRows.hasValue = hasValue;
- inputRows.hasStatement = hasStatement;
- inputRows.hasDummy = hasDummy;
- return inputRows;
- };
- /**
- * Draw the path of the block.
- * Move the fields to the correct locations.
- * @param {number} iconWidth Offset of first row due to icons.
- * @param {!Array.<!Array.<!Object>>} inputRows 2D array of objects, each
- * containing position information.
- * @private
- */
- Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) {
- this.startHat_ = false;
- // Reset the height to zero and let the rendering process add in
- // portions of the block height as it goes. (e.g. hats, inputs, etc.)
- this.height = 0;
- // Should the top and bottom left corners be rounded or square?
- if (this.outputConnection) {
- this.squareTopLeftCorner_ = true;
- this.squareBottomLeftCorner_ = true;
- } else {
- this.squareTopLeftCorner_ = false;
- this.squareBottomLeftCorner_ = false;
- // If this block is in the middle of a stack, square the corners.
- if (this.previousConnection) {
- var prevBlock = this.previousConnection.targetBlock();
- if (prevBlock && prevBlock.getNextBlock() == this) {
- this.squareTopLeftCorner_ = true;
- }
- } else if (Blockly.BlockSvg.START_HAT) {
- // No output or previous connection.
- this.squareTopLeftCorner_ = true;
- this.startHat_ = true;
- this.height += Blockly.BlockSvg.START_HAT_HEIGHT;
- inputRows.rightEdge = Math.max(inputRows.rightEdge, 100);
- }
- var nextBlock = this.getNextBlock();
- if (nextBlock) {
- this.squareBottomLeftCorner_ = true;
- }
- }
- // Assemble the block's path.
- var steps = [];
- var inlineSteps = [];
- // The highlighting applies to edges facing the upper-left corner.
- // Since highlighting is a two-pixel wide border, it would normally overhang
- // the edge of the block by a pixel. So undersize all measurements by a pixel.
- var highlightSteps = [];
- var highlightInlineSteps = [];
- this.renderDrawTop_(steps, highlightSteps, inputRows.rightEdge);
- var cursorY = this.renderDrawRight_(steps, highlightSteps, inlineSteps,
- highlightInlineSteps, inputRows, iconWidth);
- this.renderDrawBottom_(steps, highlightSteps, cursorY);
- this.renderDrawLeft_(steps, highlightSteps);
- var pathString = steps.join(' ') + '\n' + inlineSteps.join(' ');
- this.svgPath_.setAttribute('d', pathString);
- this.svgPathDark_.setAttribute('d', pathString);
- pathString = highlightSteps.join(' ') + '\n' + highlightInlineSteps.join(' ');
- this.svgPathLight_.setAttribute('d', pathString);
- if (this.RTL) {
- // Mirror the block's path.
- this.svgPath_.setAttribute('transform', 'scale(-1 1)');
- this.svgPathLight_.setAttribute('transform', 'scale(-1 1)');
- this.svgPathDark_.setAttribute('transform', 'translate(1,1) scale(-1 1)');
- }
- };
- /**
- * Update all of the connections on this block with the new locations calculated
- * in renderCompute. Also move all of the connected blocks based on the new
- * connection locations.
- * @private
- */
- Blockly.BlockSvg.prototype.renderMoveConnections_ = function() {
- var blockTL = this.getRelativeToSurfaceXY();
- // Don't tighten previous or output connecitons because they are inferior
- // connections.
- if (this.previousConnection) {
- this.previousConnection.moveToOffset(blockTL);
- }
- if (this.outputConnection) {
- this.outputConnection.moveToOffset(blockTL);
- }
- for (var i = 0; i < this.inputList.length; i++) {
- var conn = this.inputList[i].connection;
- if (conn) {
- conn.moveToOffset(blockTL);
- if (conn.isConnected()) {
- conn.tighten_();
- }
- }
- }
- if (this.nextConnection) {
- this.nextConnection.moveToOffset(blockTL);
- if (this.nextConnection.isConnected()) {
- this.nextConnection.tighten_();
- }
- }
- };
- /**
- * Render the top edge of the block.
- * @param {!Array.<string>} steps Path of block outline.
- * @param {!Array.<string>} highlightSteps Path of block highlights.
- * @param {number} rightEdge Minimum width of block.
- * @private
- */
- Blockly.BlockSvg.prototype.renderDrawTop_ =
- function(steps, highlightSteps, rightEdge) {
- /* eslint-disable indent */
- // Position the cursor at the top-left starting point.
- if (this.squareTopLeftCorner_) {
- steps.push('m 0,0');
- highlightSteps.push('m 0.5,0.5');
- if (this.startHat_) {
- steps.push(Blockly.BlockSvg.START_HAT_PATH);
- highlightSteps.push(this.RTL ?
- Blockly.BlockSvg.START_HAT_HIGHLIGHT_RTL :
- Blockly.BlockSvg.START_HAT_HIGHLIGHT_LTR);
- }
- } else {
- steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_START);
- highlightSteps.push(this.RTL ?
- Blockly.BlockSvg.TOP_LEFT_CORNER_START_HIGHLIGHT_RTL :
- Blockly.BlockSvg.TOP_LEFT_CORNER_START_HIGHLIGHT_LTR);
- // Top-left rounded corner.
- steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER);
- highlightSteps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_HIGHLIGHT);
- }
- // Top edge.
- if (this.previousConnection) {
- steps.push('H', Blockly.BlockSvg.NOTCH_WIDTH - 15);
- highlightSteps.push('H', Blockly.BlockSvg.NOTCH_WIDTH - 15);
- steps.push(Blockly.BlockSvg.NOTCH_PATH_LEFT);
- highlightSteps.push(Blockly.BlockSvg.NOTCH_PATH_LEFT_HIGHLIGHT);
- var connectionX = (this.RTL ?
- -Blockly.BlockSvg.NOTCH_WIDTH : Blockly.BlockSvg.NOTCH_WIDTH);
- this.previousConnection.setOffsetInBlock(connectionX, 0);
- }
- steps.push('H', rightEdge);
- highlightSteps.push('H', rightEdge - 0.5);
- this.width = rightEdge;
- }; /* eslint-enable indent */
- /**
- * Render the right edge of the block.
- * @param {!Array.<string>} steps Path of block outline.
- * @param {!Array.<string>} highlightSteps Path of block highlights.
- * @param {!Array.<string>} inlineSteps Inline block outlines.
- * @param {!Array.<string>} highlightInlineSteps Inline block highlights.
- * @param {!Array.<!Array.<!Object>>} inputRows 2D array of objects, each
- * containing position information.
- * @param {number} iconWidth Offset of first row due to icons.
- * @return {number} Height of block.
- * @private
- */
- Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps,
- inlineSteps, highlightInlineSteps, inputRows, iconWidth) {
- var cursorX;
- var cursorY = 0;
- var connectionX, connectionY;
- for (var y = 0, row; row = inputRows[y]; y++) {
- cursorX = Blockly.BlockSvg.SEP_SPACE_X;
- if (y == 0) {
- cursorX += this.RTL ? -iconWidth : iconWidth;
- }
- highlightSteps.push('M', (inputRows.rightEdge - 0.5) + ',' +
- (cursorY + 0.5));
- if (this.isCollapsed()) {
- // Jagged right edge.
- var input = row[0];
- var fieldX = cursorX;
- var fieldY = cursorY;
- this.renderFields_(input.fieldRow, fieldX, fieldY);
- steps.push(Blockly.BlockSvg.JAGGED_TEETH);
- highlightSteps.push('h 8');
- var remainder = row.height - Blockly.BlockSvg.JAGGED_TEETH_HEIGHT;
- steps.push('v', remainder);
- if (this.RTL) {
- highlightSteps.push('v 3.9 l 7.2,3.4 m -14.5,8.9 l 7.3,3.5');
- highlightSteps.push('v', remainder - 0.7);
- }
- this.width += Blockly.BlockSvg.JAGGED_TEETH_WIDTH;
- } else if (row.type == Blockly.BlockSvg.INLINE) {
- // Inline inputs.
- for (var x = 0, input; input = row[x]; x++) {
- var fieldX = cursorX;
- var fieldY = cursorY;
- if (row.thicker) {
- // Lower the field slightly.
- fieldY += Blockly.BlockSvg.INLINE_PADDING_Y;
- }
- // TODO: Align inline field rows (left/right/centre).
- cursorX = this.renderFields_(input.fieldRow, fieldX, fieldY);
- if (input.type != Blockly.DUMMY_INPUT) {
- cursorX += input.renderWidth + Blockly.BlockSvg.SEP_SPACE_X;
- }
- if (input.type == Blockly.INPUT_VALUE) {
- inlineSteps.push('M', (cursorX - Blockly.BlockSvg.SEP_SPACE_X) +
- ',' + (cursorY + Blockly.BlockSvg.INLINE_PADDING_Y));
- inlineSteps.push('h', Blockly.BlockSvg.TAB_WIDTH - 2 -
- input.renderWidth);
- inlineSteps.push(Blockly.BlockSvg.TAB_PATH_DOWN);
- inlineSteps.push('v', input.renderHeight + 1 -
- Blockly.BlockSvg.TAB_HEIGHT);
- inlineSteps.push('h', input.renderWidth + 2 -
- Blockly.BlockSvg.TAB_WIDTH);
- inlineSteps.push('z');
- if (this.RTL) {
- // Highlight right edge, around back of tab, and bottom.
- highlightInlineSteps.push('M',
- (cursorX - Blockly.BlockSvg.SEP_SPACE_X - 2.5 +
- Blockly.BlockSvg.TAB_WIDTH - input.renderWidth) + ',' +
- (cursorY + Blockly.BlockSvg.INLINE_PADDING_Y + 0.5));
- highlightInlineSteps.push(
- Blockly.BlockSvg.TAB_PATH_DOWN_HIGHLIGHT_RTL);
- highlightInlineSteps.push('v',
- input.renderHeight - Blockly.BlockSvg.TAB_HEIGHT + 2.5);
- highlightInlineSteps.push('h',
- input.renderWidth - Blockly.BlockSvg.TAB_WIDTH + 2);
- } else {
- // Highlight right edge, bottom.
- highlightInlineSteps.push('M',
- (cursorX - Blockly.BlockSvg.SEP_SPACE_X + 0.5) + ',' +
- (cursorY + Blockly.BlockSvg.INLINE_PADDING_Y + 0.5));
- highlightInlineSteps.push('v', input.renderHeight + 1);
- highlightInlineSteps.push('h', Blockly.BlockSvg.TAB_WIDTH - 2 -
- input.renderWidth);
- // Short highlight glint at bottom of tab.
- highlightInlineSteps.push('M',
- (cursorX - input.renderWidth - Blockly.BlockSvg.SEP_SPACE_X +
- 0.9) + ',' + (cursorY + Blockly.BlockSvg.INLINE_PADDING_Y +
- Blockly.BlockSvg.TAB_HEIGHT - 0.7));
- highlightInlineSteps.push('l',
- (Blockly.BlockSvg.TAB_WIDTH * 0.46) + ',-2.1');
- }
- // Create inline input connection.
- if (this.RTL) {
- connectionX = -cursorX -
- Blockly.BlockSvg.TAB_WIDTH + Blockly.BlockSvg.SEP_SPACE_X +
- input.renderWidth + 1;
- } else {
- connectionX = cursorX +
- Blockly.BlockSvg.TAB_WIDTH - Blockly.BlockSvg.SEP_SPACE_X -
- input.renderWidth - 1;
- }
- connectionY = cursorY + Blockly.BlockSvg.INLINE_PADDING_Y + 1;
- input.connection.setOffsetInBlock(connectionX, connectionY);
- }
- }
- cursorX = Math.max(cursorX, inputRows.rightEdge);
- this.width = Math.max(this.width, cursorX);
- steps.push('H', cursorX);
- highlightSteps.push('H', cursorX - 0.5);
- steps.push('v', row.height);
- if (this.RTL) {
- highlightSteps.push('v', row.height - 1);
- }
- } else if (row.type == Blockly.INPUT_VALUE) {
- // External input.
- var input = row[0];
- var fieldX = cursorX;
- var fieldY = cursorY;
- if (input.align != Blockly.ALIGN_LEFT) {
- var fieldRightX = inputRows.rightEdge - input.fieldWidth -
- Blockly.BlockSvg.TAB_WIDTH - 2 * Blockly.BlockSvg.SEP_SPACE_X;
- if (input.align == Blockly.ALIGN_RIGHT) {
- fieldX += fieldRightX;
- } else if (input.align == Blockly.ALIGN_CENTRE) {
- fieldX += fieldRightX / 2;
- }
- }
- this.renderFields_(input.fieldRow, fieldX, fieldY);
- steps.push(Blockly.BlockSvg.TAB_PATH_DOWN);
- var v = row.height - Blockly.BlockSvg.TAB_HEIGHT;
- steps.push('v', v);
- if (this.RTL) {
- // Highlight around back of tab.
- highlightSteps.push(Blockly.BlockSvg.TAB_PATH_DOWN_HIGHLIGHT_RTL);
- highlightSteps.push('v', v + 0.5);
- } else {
- // Short highlight glint at bottom of tab.
- highlightSteps.push('M', (inputRows.rightEdge - 5) + ',' +
- (cursorY + Blockly.BlockSvg.TAB_HEIGHT - 0.7));
- highlightSteps.push('l', (Blockly.BlockSvg.TAB_WIDTH * 0.46) +
- ',-2.1');
- }
- // Create external input connection.
- connectionX = this.RTL ? -inputRows.rightEdge - 1 :
- inputRows.rightEdge + 1;
- input.connection.setOffsetInBlock(connectionX, cursorY);
- if (input.connection.isConnected()) {
- this.width = Math.max(this.width, inputRows.rightEdge +
- input.connection.targetBlock().getHeightWidth().width -
- Blockly.BlockSvg.TAB_WIDTH + 1);
- }
- } else if (row.type == Blockly.DUMMY_INPUT) {
- // External naked field.
- var input = row[0];
- var fieldX = cursorX;
- var fieldY = cursorY;
- if (input.align != Blockly.ALIGN_LEFT) {
- var fieldRightX = inputRows.rightEdge - input.fieldWidth -
- 2 * Blockly.BlockSvg.SEP_SPACE_X;
- if (inputRows.hasValue) {
- fieldRightX -= Blockly.BlockSvg.TAB_WIDTH;
- }
- if (input.align == Blockly.ALIGN_RIGHT) {
- fieldX += fieldRightX;
- } else if (input.align == Blockly.ALIGN_CENTRE) {
- fieldX += fieldRightX / 2;
- }
- }
- this.renderFields_(input.fieldRow, fieldX, fieldY);
- steps.push('v', row.height);
- if (this.RTL) {
- highlightSteps.push('v', row.height - 1);
- }
- } else if (row.type == Blockly.NEXT_STATEMENT) {
- // Nested statement.
- var input = row[0];
- if (y == 0) {
- // If the first input is a statement stack, add a small row on top.
- steps.push('v', Blockly.BlockSvg.SEP_SPACE_Y);
- if (this.RTL) {
- highlightSteps.push('v', Blockly.BlockSvg.SEP_SPACE_Y - 1);
- }
- cursorY += Blockly.BlockSvg.SEP_SPACE_Y;
- }
- var fieldX = cursorX;
- var fieldY = cursorY;
- if (input.align != Blockly.ALIGN_LEFT) {
- var fieldRightX = inputRows.statementEdge - input.fieldWidth -
- 2 * Blockly.BlockSvg.SEP_SPACE_X;
- if (input.align == Blockly.ALIGN_RIGHT) {
- fieldX += fieldRightX;
- } else if (input.align == Blockly.ALIGN_CENTRE) {
- fieldX += fieldRightX / 2;
- }
- }
- this.renderFields_(input.fieldRow, fieldX, fieldY);
- cursorX = inputRows.statementEdge + Blockly.BlockSvg.NOTCH_WIDTH;
- steps.push('H', cursorX);
- steps.push(Blockly.BlockSvg.INNER_TOP_LEFT_CORNER);
- steps.push('v', row.height - 2 * Blockly.BlockSvg.CORNER_RADIUS);
- steps.push(Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER);
- steps.push('H', inputRows.rightEdge);
- if (this.RTL) {
- highlightSteps.push('M',
- (cursorX - Blockly.BlockSvg.NOTCH_WIDTH +
- Blockly.BlockSvg.DISTANCE_45_OUTSIDE) +
- ',' + (cursorY + Blockly.BlockSvg.DISTANCE_45_OUTSIDE));
- highlightSteps.push(
- Blockly.BlockSvg.INNER_TOP_LEFT_CORNER_HIGHLIGHT_RTL);
- highlightSteps.push('v',
- row.height - 2 * Blockly.BlockSvg.CORNER_RADIUS);
- highlightSteps.push(
- Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_RTL);
- highlightSteps.push('H', inputRows.rightEdge - 0.5);
- } else {
- highlightSteps.push('M',
- (cursorX - Blockly.BlockSvg.NOTCH_WIDTH +
- Blockly.BlockSvg.DISTANCE_45_OUTSIDE) + ',' +
- (cursorY + row.height - Blockly.BlockSvg.DISTANCE_45_OUTSIDE));
- highlightSteps.push(
- Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_LTR);
- highlightSteps.push('H', inputRows.rightEdge - 0.5);
- }
- // Create statement connection.
- connectionX = this.RTL ? -cursorX : cursorX + 1;
- input.connection.setOffsetInBlock(connectionX, cursorY + 1);
- if (input.connection.isConnected()) {
- this.width = Math.max(this.width, inputRows.statementEdge +
- input.connection.targetBlock().getHeightWidth().width);
- }
- if (y == inputRows.length - 1 ||
- inputRows[y + 1].type == Blockly.NEXT_STATEMENT) {
- // If the final input is a statement stack, add a small row underneath.
- // Consecutive statement stacks are also separated by a small divider.
- steps.push('v', Blockly.BlockSvg.SEP_SPACE_Y);
- if (this.RTL) {
- highlightSteps.push('v', Blockly.BlockSvg.SEP_SPACE_Y - 1);
- }
- cursorY += Blockly.BlockSvg.SEP_SPACE_Y;
- }
- }
- cursorY += row.height;
- }
- if (!inputRows.length) {
- cursorY = Blockly.BlockSvg.MIN_BLOCK_Y;
- steps.push('V', cursorY);
- if (this.RTL) {
- highlightSteps.push('V', cursorY - 1);
- }
- }
- return cursorY;
- };
- /**
- * Render the bottom edge of the block.
- * @param {!Array.<string>} steps Path of block outline.
- * @param {!Array.<string>} highlightSteps Path of block highlights.
- * @param {number} cursorY Height of block.
- * @private
- */
- Blockly.BlockSvg.prototype.renderDrawBottom_ =
- function(steps, highlightSteps, cursorY) {
- /* eslint-disable indent */
- this.height += cursorY + 1; // Add one for the shadow.
- if (this.nextConnection) {
- steps.push('H', (Blockly.BlockSvg.NOTCH_WIDTH + (this.RTL ? 0.5 : - 0.5)) +
- ' ' + Blockly.BlockSvg.NOTCH_PATH_RIGHT);
- // Create next block connection.
- var connectionX;
- if (this.RTL) {
- connectionX = -Blockly.BlockSvg.NOTCH_WIDTH;
- } else {
- connectionX = Blockly.BlockSvg.NOTCH_WIDTH;
- }
- this.nextConnection.setOffsetInBlock(connectionX, cursorY + 1);
- this.height += 4; // Height of tab.
- }
- // Should the bottom-left corner be rounded or square?
- if (this.squareBottomLeftCorner_) {
- steps.push('H 0');
- if (!this.RTL) {
- highlightSteps.push('M', '0.5,' + (cursorY - 0.5));
- }
- } else {
- steps.push('H', Blockly.BlockSvg.CORNER_RADIUS);
- steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' +
- Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 -' +
- Blockly.BlockSvg.CORNER_RADIUS + ',-' +
- Blockly.BlockSvg.CORNER_RADIUS);
- if (!this.RTL) {
- highlightSteps.push('M', Blockly.BlockSvg.DISTANCE_45_INSIDE + ',' +
- (cursorY - Blockly.BlockSvg.DISTANCE_45_INSIDE));
- highlightSteps.push('A', (Blockly.BlockSvg.CORNER_RADIUS - 0.5) + ',' +
- (Blockly.BlockSvg.CORNER_RADIUS - 0.5) + ' 0 0,1 ' +
- '0.5,' + (cursorY - Blockly.BlockSvg.CORNER_RADIUS));
- }
- }
- }; /* eslint-enable indent */
- /**
- * Render the left edge of the block.
- * @param {!Array.<string>} steps Path of block outline.
- * @param {!Array.<string>} highlightSteps Path of block highlights.
- * @private
- */
- Blockly.BlockSvg.prototype.renderDrawLeft_ = function(steps, highlightSteps) {
- if (this.outputConnection) {
- // Create output connection.
- this.outputConnection.setOffsetInBlock(0, 0);
- steps.push('V', Blockly.BlockSvg.TAB_HEIGHT);
- steps.push('c 0,-10 -' + Blockly.BlockSvg.TAB_WIDTH + ',8 -' +
- Blockly.BlockSvg.TAB_WIDTH + ',-7.5 s ' + Blockly.BlockSvg.TAB_WIDTH +
- ',2.5 ' + Blockly.BlockSvg.TAB_WIDTH + ',-7.5');
- if (this.RTL) {
- highlightSteps.push('M', (Blockly.BlockSvg.TAB_WIDTH * -0.25) + ',8.4');
- highlightSteps.push('l', (Blockly.BlockSvg.TAB_WIDTH * -0.45) + ',-2.1');
- } else {
- highlightSteps.push('V', Blockly.BlockSvg.TAB_HEIGHT - 1.5);
- highlightSteps.push('m', (Blockly.BlockSvg.TAB_WIDTH * -0.92) +
- ',-0.5 q ' + (Blockly.BlockSvg.TAB_WIDTH * -0.19) +
- ',-5.5 0,-11');
- highlightSteps.push('m', (Blockly.BlockSvg.TAB_WIDTH * 0.92) +
- ',1 V 0.5 H 1');
- }
- this.width += Blockly.BlockSvg.TAB_WIDTH;
- } else if (!this.RTL) {
- if (this.squareTopLeftCorner_) {
- // Statement block in a stack.
- highlightSteps.push('V', 0.5);
- } else {
- highlightSteps.push('V', Blockly.BlockSvg.CORNER_RADIUS);
- }
- }
- steps.push('z');
- };
|