From 18a1550285b3e2990fb90c0f70703f21a0cce683 Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Thu, 17 Mar 2016 15:46:22 -0700 Subject: [PATCH 01/32] Horizontal toolbox layout with positioning at start or end. --- core/blockly.js | 2 +- core/constants.js | 25 ++ core/css.js | 18 ++ core/flyout.js | 497 +++++++++++++++++++++++++++--------- core/inject.js | 2 +- core/options.js | 31 ++- core/toolbox.js | 193 +++++++++----- core/workspace.js | 2 + core/workspace_svg.js | 4 +- tests/multi_playground.html | 462 +++++++++++++++++++++++++++++++++ 10 files changed, 1045 insertions(+), 191 deletions(-) create mode 100644 tests/multi_playground.html diff --git a/core/blockly.js b/core/blockly.js index 36525509..b7adeb07 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -445,7 +445,7 @@ Blockly.getMainWorkspaceMetrics_ = function() { var bottomEdge = topEdge + blockBox.height; } var absoluteLeft = 0; - if (!this.RTL && this.toolbox_) { + if (this.toolbox_ && this.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { absoluteLeft = this.toolbox_.width; } var metrics = { diff --git a/core/constants.js b/core/constants.js index 9869a7f9..27de6cea 100644 --- a/core/constants.js +++ b/core/constants.js @@ -162,3 +162,28 @@ Blockly.OPPOSITE_TYPE[Blockly.INPUT_VALUE] = Blockly.OUTPUT_VALUE; Blockly.OPPOSITE_TYPE[Blockly.OUTPUT_VALUE] = Blockly.INPUT_VALUE; Blockly.OPPOSITE_TYPE[Blockly.NEXT_STATEMENT] = Blockly.PREVIOUS_STATEMENT; Blockly.OPPOSITE_TYPE[Blockly.PREVIOUS_STATEMENT] = Blockly.NEXT_STATEMENT; + + +/** + * ENUM for toolbox and flyout at top of screen. + * @const + */ +Blockly.TOOLBOX_AT_TOP = 0; + +/** + * ENUM for toolbox and flyout at bottom of screen. + * @const + */ +Blockly.TOOLBOX_AT_BOTTOM = 1; + +/** + * ENUM for toolbox and flyout at left of screen. + * @const + */ +Blockly.TOOLBOX_AT_LEFT = 2; + +/** + * ENUM for toolbox and flyout at right of screen. + * @const + */ +Blockly.TOOLBOX_AT_RIGHT = 3; diff --git a/core/css.js b/core/css.js index 7e6f8be9..ac416dc7 100644 --- a/core/css.js +++ b/core/css.js @@ -430,6 +430,16 @@ Blockly.Css.CONTENT = [ 'white-space: nowrap;', '}', + '.blocklyHorizontalTree {', + 'float: left;', + 'margin: 1px 5px 8px 0px;', + '}', + + '.blocklyHorizontalTreeRtl {', + 'float: right;', + 'margin: 1px 0px 8px 5px;', + '}', + '.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {', 'margin-left: 8px;', '}', @@ -444,6 +454,14 @@ Blockly.Css.CONTENT = [ 'margin: 5px 0;', '}', + '.blocklyTreeSeparatorHorizontal {', + 'border-right: solid #e5e5e5 1px;', + 'width: 0px;', + 'padding: 5px 0;', + 'margin: 0 5px;', + '}', + + '.blocklyTreeIcon {', 'background-image: url(<<>>/sprites.png);', 'height: 16px;', diff --git a/core/flyout.js b/core/flyout.js index 84bcbb81..a665e335 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -56,6 +56,18 @@ Blockly.Flyout = function(workspaceOptions) { */ this.RTL = !!workspaceOptions.RTL; + /** + * Flyout should be laid out horizontally vs vertically. + * @type {boolean} + */ + this.horizontalLayout_ = workspaceOptions.horizontalLayout; + + /** + * Position of the toolbox and flyout relative to the workspace. + * @type {number} + */ + this.toolboxPosition_ = workspaceOptions.toolboxPosition; + /** * Opaque data that can be passed to Blockly.unbindEvent_. * @type {!Array.} @@ -120,6 +132,13 @@ Blockly.Flyout.prototype.width_ = 0; */ Blockly.Flyout.prototype.height_ = 0; +/** + * Vertical offset of flyout. + * @type {number} + * @private + */ +Blockly.Flyout.prototype.verticalOffset_ = 0; + /** * Creates the flyout's DOM. Only needs to be called once. * @return {!Element} The flyout's SVG group. @@ -148,7 +167,8 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) { this.targetWorkspace_ = targetWorkspace; this.workspace_.targetWorkspace = targetWorkspace; // Add scrollbar. - this.scrollbar_ = new Blockly.Scrollbar(this.workspace_, false, false); + this.scrollbar_ = new Blockly.Scrollbar(this.workspace_, + this.horizontalLayout_, false); this.hide(); @@ -197,9 +217,12 @@ Blockly.Flyout.prototype.dispose = function() { * .viewHeight: Height of the visible rectangle, * .viewWidth: Width of the visible rectangle, * .contentHeight: Height of the contents, + * .contentWidth: Width of the contents, * .viewTop: Offset of top edge of visible rectangle from parent, * .contentTop: Offset of the top-most content from the y=0 coordinate, * .absoluteTop: Top-edge of view. + * .viewLeft: Offset of the left edge of visible rectangle from parent, + * .contentLeft: Offset of the left-most content from the x=0 coordinate, * .absoluteLeft: Left-edge of view. * @return {Object} Contains size and position metrics of the flyout. * @private @@ -209,44 +232,77 @@ Blockly.Flyout.prototype.getMetrics_ = function() { // Flyout is hidden. return null; } - var viewHeight = this.height_ - 2 * this.SCROLLBAR_PADDING; - var viewWidth = this.width_; + try { var optionBox = this.workspace_.getCanvas().getBBox(); } catch (e) { // Firefox has trouble with hidden elements (Bug 528969). var optionBox = {height: 0, y: 0}; } - return { + + var absoluteTop = this.verticalOffset_ + this.SCROLLBAR_PADDING + if (this.horizontalLayout_) { + if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { + absoluteTop = 0; + } + var viewHeight = this.height_; + var viewWidth = this.width_ - 2 * this.SCROLLBAR_PADDING; + } else { + var viewHeight = this.height_ - 2 * this.SCROLLBAR_PADDING; + var viewWidth = this.width_; + } + + var metrics = { viewHeight: viewHeight, viewWidth: viewWidth, - contentHeight: (optionBox.height + optionBox.y) * this.workspace_.scale, + contentHeight: (optionBox.height) * this.workspace_.scale, + contentWidth: (optionBox.width) * this.workspace_.scale, viewTop: -this.workspace_.scrollY, - contentTop: 0, - absoluteTop: this.SCROLLBAR_PADDING, - absoluteLeft: 0 + viewLeft: -this.workspace_.scrollX, + contentTop: optionBox.y, + contentLeft: 0, + absoluteTop: absoluteTop, + absoluteLeft: this.SCROLLBAR_PADDING }; + return metrics; }; /** - * Sets the Y translation of the flyout to match the scrollbars. - * @param {!Object} yRatio Contains a y property which is a float - * between 0 and 1 specifying the degree of scrolling. + * Sets the translation of the flyout to match the scrollbars. + * @param {!Object} xyRatio Contains a y property which is a float + * between 0 and 1 specifying the degree of scrolling and a + * similar x property. * @private */ -Blockly.Flyout.prototype.setMetrics_ = function(yRatio) { +Blockly.Flyout.prototype.setMetrics_ = function(xyRatio) { var metrics = this.getMetrics_(); // This is a fix to an apparent race condition. if (!metrics) { return; } - if (goog.isNumber(yRatio.y)) { + if (!this.horizontalLayout_ && goog.isNumber(xyRatio.y)) { this.workspace_.scrollY = - -metrics.contentHeight * yRatio.y - metrics.contentTop; + -metrics.contentHeight * xyRatio.y - metrics.contentTop; + } else if (this.horizontalLayout_ && goog.isNumber(xyRatio.x)) { + if (this.RTL) { + this.workspace_.scrollX = + -metrics.contentWidth * xyRatio.x + metrics.contentLeft; + } else { + this.workspace_.scrollX = + -metrics.contentWidth * xyRatio.x - metrics.contentLeft; + } } - this.workspace_.translate(0, this.workspace_.scrollY + metrics.absoluteTop); + var translateX = this.horizontalLayout_ && this.RTL ? + metrics.absoluteLeft + metrics.viewWidth - this.workspace_.scrollX : + this.workspace_.scrollX + metrics.absoluteLeft; + this.workspace_.translate(translateX, + this.workspace_.scrollY + metrics.absoluteTop); }; +Blockly.Flyout.prototype.setVerticalOffset = function(verticalOffset) { + this.verticalOffset_ = verticalOffset; +} + /** * Move the toolbox to the edge of the workspace. */ @@ -259,47 +315,144 @@ Blockly.Flyout.prototype.position = function() { // Hidden components will return null. return; } - var edgeWidth = this.width_ - this.CORNER_RADIUS; - if (this.RTL) { + var edgeWidth = this.horizontalLayout_ ? metrics.viewWidth : this.width_; + edgeWidth -= this.CORNER_RADIUS; + if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { edgeWidth *= -1; } - var path = ['M ' + (this.RTL ? this.width_ : 0) + ',0']; - path.push('h', edgeWidth); - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, - this.RTL ? 0 : 1, - this.RTL ? -this.CORNER_RADIUS : this.CORNER_RADIUS, - this.CORNER_RADIUS); - path.push('v', Math.max(0, metrics.viewHeight - this.CORNER_RADIUS * 2)); - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, - this.RTL ? 0 : 1, - this.RTL ? this.CORNER_RADIUS : -this.CORNER_RADIUS, - this.CORNER_RADIUS); - path.push('h', -edgeWidth); - path.push('z'); - this.svgBackground_.setAttribute('d', path.join(' ')); + + this.setBackgroundPath_(edgeWidth, + this.horizontalLayout_ ? this.height_ + this.verticalOffset_ : metrics.viewHeight); var x = metrics.absoluteLeft; - if (this.RTL) { + if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { x += metrics.viewWidth; x -= this.width_; } - this.svgGroup_.setAttribute('transform', - 'translate(' + x + ',' + metrics.absoluteTop + ')'); - // Record the height for Blockly.Flyout.getMetrics_. - this.height_ = metrics.viewHeight; + var y = metrics.absoluteTop; + if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { + y += metrics.viewHeight; + y -= this.height_; + } + + this.svgGroup_.setAttribute('transform', + 'translate(' + x + ',' + y + ')'); + + // Record the height for Blockly.Flyout.getMetrics_, or width if the layout is + // horizontal. + if (this.horizontalLayout_) { + this.width_ = metrics.viewWidth; + } else { + this.height_ = metrics.viewHeight; + } // Update the scrollbar (if one exists). if (this.scrollbar_) { this.scrollbar_.resize(); } + // The blocks need to be visible in order to be laid out and measured + // correctly, but we don't want the flyout to show up until it's properly + // sized. Opacity is set to zero in show(). + this.svgGroup_.style.opacity = 1; +}; + +/** + * Create and set the path for the visible boundaries of the toolbox. + * @param {number} width The width of the toolbox, not including the + * rounded corners. + * @param {number} height The height of the toolbox, not including + * rounded corners. + * @private + */ +Blockly.Flyout.prototype.setBackgroundPath_ = function(width, height) { + if (this.horizontalLayout_) { + this.setBackgroundPathHorizontal_(width, height); + } else { + this.setBackgroundPathVertical_(width, height); + } +}; + +/** + * Create and set the path for the visible boundaries of the toolbox in vertical mode. + * @param {number} width The width of the toolbox, not including the + * rounded corners. + * @param {number} height The height of the toolbox, not including + * rounded corners. + * @private + */ +Blockly.Flyout.prototype.setBackgroundPathVertical_ = function(width, height) { + var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT; + // Decide whether to start on the left or right. + var path = ['M ' + (atRight ? this.width_ : 0) + ',0']; + // Top. + path.push('h', width); + // Rounded corner. + path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, + atRight ? 0 : 1, + atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS, + this.CORNER_RADIUS); + // Side closest to workspace. + path.push('v', Math.max(0, height - this.CORNER_RADIUS * 2)); + // Rounded corner. + path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, + atRight ? 0 : 1, + atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS, + this.CORNER_RADIUS); + // Bottom. + path.push('h', -width); + path.push('z'); + this.svgBackground_.setAttribute('d', path.join(' ')); +}; + +/** + * Create and set the path for the visible boundaries of the toolbox in horizontal mode. + * @param {number} width The width of the toolbox, not including the + * rounded corners. + * @param {number} height The height of the toolbox, not including + * rounded corners. + * @private + */ +Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = function(width, height) { + var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP; + // start at top left. + var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)]; + + if (atTop) { + // top + path.push('h', width + this.CORNER_RADIUS); + // right + path.push('v', height); + // bottom + path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, + -this.CORNER_RADIUS, this.CORNER_RADIUS); + path.push('h', -1 * (width - this.CORNER_RADIUS)); + // left + path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, + -this.CORNER_RADIUS, -this.CORNER_RADIUS); + path.push('z'); + } else { + // top + path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, + this.CORNER_RADIUS, -this.CORNER_RADIUS); + path.push('h', width - this.CORNER_RADIUS); + // right + path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, + this.CORNER_RADIUS, this.CORNER_RADIUS); + path.push('v', height - this.CORNER_RADIUS); + // bottom + path.push('h', -width - this.CORNER_RADIUS); + // left + path.push('z'); + } + this.svgBackground_.setAttribute('d', path.join(' ')); }; /** * Scroll the flyout to the top. */ Blockly.Flyout.prototype.scrollToStart = function() { - this.scrollbar_.set(0); + this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? 1000000000 : 0); }; /** @@ -308,6 +461,10 @@ Blockly.Flyout.prototype.scrollToStart = function() { * @private */ Blockly.Flyout.prototype.wheel_ = function(e) { + // Don't scroll sideways. + if (this.horizontalLayout_) { + return; + } var delta = e.deltaY; if (delta) { if (goog.userAgent.GECKO) { @@ -405,7 +562,14 @@ Blockly.Flyout.prototype.show = function(xmlList) { } } - // Lay out the blocks vertically. + // The blocks need to be visible in order to be laid out and measured + // correctly, but we don't want the flyout to show up until it's properly + // sized. Opacity is reset at the end of position(). + this.svgGroup_.style.opacity = 0; + this.svgGroup_.style.display = 'block'; + + // Lay out the blocks. + var cursorX = margin / this.workspace_.scale + Blockly.BlockSvg.TAB_WIDTH; var cursorY = margin; for (var i = 0, block; block = blocks[i]; i++) { var allBlocks = block.getDescendants(); @@ -418,10 +582,12 @@ Blockly.Flyout.prototype.show = function(xmlList) { block.render(); var root = block.getSvgRoot(); var blockHW = block.getHeightWidth(); - var x = this.RTL ? 0 : margin / this.workspace_.scale + - Blockly.BlockSvg.TAB_WIDTH; - block.moveBy(x, cursorY); - cursorY += blockHW.height + gaps[i]; + block.moveBy((this.horizontalLayout_ && this.RTL) ? -cursorX : cursorX, cursorY); + if (this.horizontalLayout_) { + cursorX += blockHW.width + gaps[i]; + } else { + cursorY += blockHW.height + gaps[i]; + } // Create an invisible rectangle under the block to act as a button. Just // using the block as a button is poor, since blocks have holes in them. @@ -431,6 +597,44 @@ Blockly.Flyout.prototype.show = function(xmlList) { block.flyoutRect_ = rect; this.buttons_[i] = rect; + this.addBlockListeners_(root, block, rect); + } + + // IE 11 is an incompetant browser that fails to fire mouseout events. + // When the mouse is over the background, deselect all blocks. + var deselectAll = function(e) { + var blocks = this.workspace_.getTopBlocks(false); + for (var i = 0, block; block = blocks[i]; i++) { + block.removeSelect(); + } + }; + this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover', + this, deselectAll)); + + if (this.horizontalLayout_) { + this.height_ = 0; + } else { + this.width_ = 0; + } + this.reflow(); + + this.filterForCapacity_(); + + // Fire a resize event to update the flyout's scrollbar. + Blockly.fireUiEventNow(window, 'resize'); + this.reflowWrapper_ = this.reflow.bind(this); + this.workspace_.addChangeListener(this.reflowWrapper_); +}; + +/** + * Add listeners to a block that has been added to the flyout. + * @param {Element} root The root node of the SVG group the block is in. + * @param {!Blockly.Block} block The block to add listeners for. + * @param {!Element} rect The invisible rectangle under the block that acts as + * a button for that block. + * @private + */ +Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) { if (this.autoClose) { this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, this.createBlockFunc_(block))); @@ -448,76 +652,6 @@ Blockly.Flyout.prototype.show = function(xmlList) { block.addSelect)); this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block, block.removeSelect)); - } - - // IE 11 is an incompetant browser that fails to fire mouseout events. - // When the mouse is over the background, deselect all blocks. - var deselectAll = function(e) { - var blocks = this.workspace_.getTopBlocks(false); - for (var i = 0, block; block = blocks[i]; i++) { - block.removeSelect(); - } - }; - this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover', - this, deselectAll)); - - this.width_ = 0; - this.reflow(); - - this.filterForCapacity_(); - - // Fire a resize event to update the flyout's scrollbar. - Blockly.fireUiEventNow(window, 'resize'); - this.reflowWrapper_ = this.reflow.bind(this); - this.workspace_.addChangeListener(this.reflowWrapper_); -}; - -/** - * Compute width of flyout. Position button under each block. - * For RTL: Lay out the blocks right-aligned. - */ -Blockly.Flyout.prototype.reflow = function() { - this.workspace_.scale = this.targetWorkspace_.scale; - var flyoutWidth = 0; - var margin = this.CORNER_RADIUS; - var blocks = this.workspace_.getTopBlocks(false); - for (var x = 0, block; block = blocks[x]; x++) { - var width = block.getHeightWidth().width; - if (block.outputConnection) { - width -= Blockly.BlockSvg.TAB_WIDTH; - } - flyoutWidth = Math.max(flyoutWidth, width); - } - flyoutWidth += Blockly.BlockSvg.TAB_WIDTH; - flyoutWidth *= this.workspace_.scale; - flyoutWidth += margin * 1.5 + Blockly.Scrollbar.scrollbarThickness; - if (this.width_ != flyoutWidth) { - for (var x = 0, block; block = blocks[x]; x++) { - var blockHW = block.getHeightWidth(); - if (this.RTL) { - // With the flyoutWidth known, right-align the blocks. - var oldX = block.getRelativeToSurfaceXY().x; - var dx = flyoutWidth - margin; - dx /= this.workspace_.scale; - dx -= Blockly.BlockSvg.TAB_WIDTH; - block.moveBy(dx - oldX, 0); - } - if (block.flyoutRect_) { - block.flyoutRect_.setAttribute('width', blockHW.width); - block.flyoutRect_.setAttribute('height', blockHW.height); - // Blocks with output tabs are shifted a bit. - var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0; - var blockXY = block.getRelativeToSurfaceXY(); - block.flyoutRect_.setAttribute('x', - this.RTL ? blockXY.x - blockHW.width + tab : blockXY.x - tab); - block.flyoutRect_.setAttribute('y', blockXY.y); - } - } - // Record the width for .getMetrics_ and .position. - this.width_ = flyoutWidth; - // Fire a resize event to update the flyout's scrollbar. - Blockly.fireUiEvent(window, 'resize'); - } }; /** @@ -578,13 +712,23 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) { * @private */ Blockly.Flyout.prototype.onMouseMove_ = function(e) { - var dy = e.clientY - this.startDragMouseY_; - this.startDragMouseY_ = e.clientY; - var metrics = this.getMetrics_(); - var y = metrics.viewTop - dy; - y = Math.min(y, metrics.contentHeight - metrics.viewHeight); - y = Math.max(y, 0); - this.scrollbar_.set(y); + if (this.horizontalLayout_) { + var dx = e.clientX - this.startDragMouseX_; + this.startDragMouseX_ = e.clientX; + var metrics = this.getMetrics_(); + var x = metrics.viewLeft - dx; + x = Math.min(x, metrics.contentWidth - metrics.viewWidth); + x = Math.max(x, 0); + this.scrollbar_.set(x); + } else { + var dy = e.clientY - this.startDragMouseY_; + this.startDragMouseY_ = e.clientY; + var metrics = this.getMetrics_(); + var y = metrics.viewTop - dy; + y = Math.min(y, metrics.contentHeight - metrics.viewHeight); + y = Math.max(y, 0); + this.scrollbar_.set(y); + } }; /** @@ -644,7 +788,7 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { } var xyOld = Blockly.getSvgXY_(svgRootOld, workspace); // Scale the scroll (getSvgXY_ did not do this). - if (flyout.RTL) { + if (flyout.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { var width = workspace.getMetrics().viewWidth - flyout.width_; xyOld.x += width / workspace.scale - width; } else { @@ -708,13 +852,24 @@ Blockly.Flyout.prototype.getClientRect = function() { // area are still deleted. Must be larger than the largest screen size, // but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE). var BIG_NUM = 1000000000; - if (this.RTL) { - var width = flyoutRect.left + flyoutRect.width + BIG_NUM; - return new goog.math.Rect(flyoutRect.left, -BIG_NUM, width, BIG_NUM * 2); + var x = flyoutRect.left; + var y = flyoutRect.top; + var width = flyoutRect.width; + var height = flyoutRect.height; + + if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) { + return new goog.math.Rect(-BIG_NUM, y - BIG_NUM, BIG_NUM * 2, + BIG_NUM + height); + } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { + return new goog.math.Rect(-BIG_NUM, y + this.verticalOffset_, BIG_NUM * 2, + BIG_NUM + height); + } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { + return new goog.math.Rect(x - BIG_NUM, -BIG_NUM, BIG_NUM + width, + BIG_NUM * 2); + } else { // Right + return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, + BIG_NUM * 2); } - // LTR - var width = BIG_NUM + flyoutRect.width + flyoutRect.left; - return new goog.math.Rect(-BIG_NUM, -BIG_NUM, width, BIG_NUM * 2); }; /** @@ -742,3 +897,93 @@ Blockly.Flyout.terminateDrag_ = function() { Blockly.Flyout.startBlock_ = null; Blockly.Flyout.startFlyout_ = null; }; + +/** + * Compute height of flyout. Position button under each block. + * For RTL: Lay out the blocks right-aligned. + */ +Blockly.Flyout.prototype.reflowHorizontal = function() { + this.workspace_.scale = this.targetWorkspace_.scale; + var flyoutHeight = 0; + var margin = this.CORNER_RADIUS; + var blocks = this.workspace_.getTopBlocks(false); + for (var x = 0, block; block = blocks[x]; x++) { + var height = block.getHeightWidth().height; + flyoutHeight = Math.max(flyoutHeight, height); + } + flyoutHeight *= this.workspace_.scale; + flyoutHeight += margin * 1.5 + Blockly.Scrollbar.scrollbarThickness; + if (this.height_ != flyoutHeight) { + for (var x = 0, block; block = blocks[x]; x++) { + var blockHW = block.getHeightWidth(); + if (block.flyoutRect_) { + block.flyoutRect_.setAttribute('width', blockHW.width); + block.flyoutRect_.setAttribute('height', blockHW.height); + // Blocks with output tabs are shifted a bit. + var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0; + var blockXY = block.getRelativeToSurfaceXY(); + block.flyoutRect_.setAttribute('y', blockXY.y); + block.flyoutRect_.setAttribute('x', + this.RTL ? blockXY.x - blockHW.width + tab : blockXY.x - tab); + } + } + // Record the width for .getMetrics_ and .position. + this.height_ = flyoutHeight; + } +}; + +/** + * Compute width of flyout. Position button under each block. + * For RTL: Lay out the blocks right-aligned. + */ +Blockly.Flyout.prototype.reflowVertical = function() { + this.workspace_.scale = this.targetWorkspace_.scale; + var flyoutWidth = 0; + var margin = this.CORNER_RADIUS; + var blocks = this.workspace_.getTopBlocks(false); + for (var x = 0, block; block = blocks[x]; x++) { + var width = block.getHeightWidth().width; + if (block.outputConnection) { + width -= Blockly.BlockSvg.TAB_WIDTH; + } + flyoutWidth = Math.max(flyoutWidth, width); + } + flyoutWidth += Blockly.BlockSvg.TAB_WIDTH; + flyoutWidth *= this.workspace_.scale; + flyoutWidth += margin * 1.5 + Blockly.Scrollbar.scrollbarThickness; + if (this.width_ != flyoutWidth) { + for (var x = 0, block; block = blocks[x]; x++) { + var blockHW = block.getHeightWidth(); + if (this.RTL) { + // With the flyoutWidth known, right-align the blocks. + var oldX = block.getRelativeToSurfaceXY().x; + var dx = flyoutWidth - margin; + dx /= this.workspace_.scale; + dx -= Blockly.BlockSvg.TAB_WIDTH; + block.moveBy(dx - oldX, 0); + } + if (block.flyoutRect_) { + block.flyoutRect_.setAttribute('width', blockHW.width); + block.flyoutRect_.setAttribute('height', blockHW.height); + // Blocks with output tabs are shifted a bit. + var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0; + var blockXY = block.getRelativeToSurfaceXY(); + block.flyoutRect_.setAttribute('x', + this.RTL ? blockXY.x - blockHW.width + tab : blockXY.x - tab); + block.flyoutRect_.setAttribute('y', blockXY.y); + } + } + // Record the width for .getMetrics_ and .position. + this.width_ = flyoutWidth; + } +}; + +Blockly.Flyout.prototype.reflow = function() { + if (this.horizontalLayout_) { + this.reflowHorizontal(); + } else { + this.reflowVertical(); + } + // Fire a resize event to update the flyout's scrollbar. + Blockly.fireUiEvent(window, 'resize'); +}; \ No newline at end of file diff --git a/core/inject.js b/core/inject.js index 0db7594c..099121d0 100644 --- a/core/inject.js +++ b/core/inject.js @@ -276,7 +276,7 @@ Blockly.init_ = function(mainWorkspace) { mainWorkspace.flyout_.show(options.languageTree.childNodes); // Translate the workspace sideways to avoid the fixed flyout. mainWorkspace.scrollX = mainWorkspace.flyout_.width_; - if (options.RTL) { + if (options.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { mainWorkspace.scrollX *= -1; } mainWorkspace.translate(mainWorkspace.scrollX, 0); diff --git a/core/options.js b/core/options.js index 91a92749..ea29430e 100644 --- a/core/options.js +++ b/core/options.js @@ -69,6 +69,26 @@ Blockly.Options = function(options) { hasSounds = true; } } + var rtl = !!options['rtl']; + var horizontalLayout = options['horizontalLayout']; + if (horizontalLayout === undefined) { + horizontalLayout = false; + } + var toolboxAtStart = options['toolboxPosition']; + if (toolboxAtStart === 'end') { + toolboxAtStart = false; + } else { + toolboxAtStart = true; + } + + if (horizontalLayout) { + var toolboxPosition = toolboxAtStart ? + Blockly.TOOLBOX_AT_TOP : Blockly.TOOLBOX_AT_BOTTOM; + } else { + var toolboxPosition = (toolboxAtStart == rtl) ? + Blockly.TOOLBOX_AT_RIGHT : Blockly.TOOLBOX_AT_LEFT; + } + var hasScrollbars = options['scrollbars']; if (hasScrollbars === undefined) { hasScrollbars = hasCategories; @@ -85,21 +105,28 @@ Blockly.Options = function(options) { pathToMedia = options['path'] + 'media/'; } - this.RTL = !!options['rtl']; + var enableRealtime = !!options['realtime']; + var realtimeOptions = enableRealtime ? options['realtimeOptions'] : undefined; + + this.RTL = rtl; this.collapse = hasCollapse; this.comments = hasComments; this.disable = hasDisable; this.readOnly = readOnly; - this.maxBlocks = options['maxBlocks'] || Infinity; + this.maxBlocks = options['maxBlocks'] || Infinity; this.pathToMedia = pathToMedia; this.hasCategories = hasCategories; this.hasScrollbars = hasScrollbars; this.hasTrashcan = hasTrashcan; this.hasSounds = hasSounds; this.hasCss = hasCss; + this.horizontalLayout = horizontalLayout; this.languageTree = languageTree; this.gridOptions = Blockly.Options.parseGridOptions_(options); this.zoomOptions = Blockly.Options.parseZoomOptions_(options); + this.enableRealtime = enableRealtime; + this.realtimeOptions = realtimeOptions; + this.toolboxPosition = toolboxPosition; }; /** diff --git a/core/toolbox.js b/core/toolbox.js index 3951c107..c3478a0b 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -31,6 +31,7 @@ goog.require('goog.dom'); goog.require('goog.events'); goog.require('goog.events.BrowserFeature'); goog.require('goog.html.SafeHtml'); +goog.require('goog.html.SafeStyle'); goog.require('goog.math.Rect'); goog.require('goog.style'); goog.require('goog.ui.tree.TreeControl'); @@ -50,14 +51,78 @@ Blockly.Toolbox = function(workspace) { * @private */ this.workspace_ = workspace; + + /** + * Is RTL vs LTR. + * @type {boolean} + */ + this.RTL = workspace.options.RTL; + + /** + * Whether the toolbox should be laid out horizontally. + * @type {boolean} + * @private + */ + this.horizontalLayout_ = workspace.options.horizontalLayout; + + /** + * Position of the toolbox and flyout relative to the workspace. + * @type {number} + */ + this.toolboxPosition = workspace.options.toolboxPosition; + + /** + * Configuration constants for Closure's tree UI. + * @type {Object.} + * @private + */ + this.config_ = { + indentWidth: 19, + cssRoot: 'blocklyTreeRoot', + cssHideRoot: 'blocklyHidden', + cssItem: '', + cssTreeRow: 'blocklyTreeRow', + cssItemLabel: 'blocklyTreeLabel', + cssTreeIcon: 'blocklyTreeIcon', + cssExpandedFolderIcon: 'blocklyTreeIconOpen', + cssFileIcon: 'blocklyTreeIconNone', + cssSelectedRow: 'blocklyTreeSelected' + }; + + + /** + * Configuration constants for tree separator. + * @type {Object.} + * @private + */ + this.treeSeparatorConfig_ = { + cssTreeRow: 'blocklyTreeSeparator' + }; + + if (this.horizontalLayout_) { + this.config_['cssTreeRow'] = + this.config_['cssTreeRow'] + + (workspace.RTL ? ' blocklyHorizontalTreeRtl' : ' blocklyHorizontalTree'); + + this.treeSeparatorConfig_['cssTreeRow'] = + 'blocklyTreeSeparatorHorizontal' + + (workspace.RTL ? ' blocklyHorizontalTreeRtl' : ' blocklyHorizontalTree'); + this.config_['cssTreeIcon'] = ''; + } }; /** - * Width of the toolbox. + * Width of the toolbox, which changes only in vertical layout. * @type {number} */ Blockly.Toolbox.prototype.width = 0; +/** + * Height of the toolbox, which changes only in horizontal layout. + * @type {number} + */ +Blockly.Toolbox.prototype.height = 0; + /** * The SVG group currently selected. * @type {SVGGElement} @@ -72,25 +137,6 @@ Blockly.Toolbox.prototype.selectedOption_ = null; */ Blockly.Toolbox.prototype.lastCategory_ = null; -/** - * Configuration constants for Closure's tree UI. - * @type {Object.} - * @const - * @private - */ -Blockly.Toolbox.prototype.CONFIG_ = { - indentWidth: 19, - cssRoot: 'blocklyTreeRoot', - cssHideRoot: 'blocklyHidden', - cssItem: '', - cssTreeRow: 'blocklyTreeRow', - cssItemLabel: 'blocklyTreeLabel', - cssTreeIcon: 'blocklyTreeIcon', - cssExpandedFolderIcon: 'blocklyTreeIconOpen', - cssFileIcon: 'blocklyTreeIconNone', - cssSelectedRow: 'blocklyTreeSelected' -}; - /** * Initializes the toolbox. */ @@ -116,7 +162,9 @@ Blockly.Toolbox.prototype.init = function() { var workspaceOptions = { disabledPatternId: workspace.options.disabledPatternId, parentWorkspace: workspace, - RTL: workspace.RTL + RTL: workspace.RTL, + horizontalLayout: workspace.horizontalLayout, + toolboxPosition: workspace.options.toolboxPosition }; /** * @type {!Blockly.Flyout} @@ -126,10 +174,10 @@ Blockly.Toolbox.prototype.init = function() { goog.dom.insertSiblingAfter(this.flyout_.createDom(), workspace.svgGroup_); this.flyout_.init(workspace); - this.CONFIG_['cleardotPath'] = workspace.options.pathToMedia + '1x1.gif'; - this.CONFIG_['cssCollapsedFolderIcon'] = + this.config_['cleardotPath'] = workspace.options.pathToMedia + '1x1.gif'; + this.config_['cssCollapsedFolderIcon'] = 'blocklyTreeIconClosed' + (workspace.RTL ? 'Rtl' : 'Ltr'); - var tree = new Blockly.Toolbox.TreeControl(this, this.CONFIG_); + var tree = new Blockly.Toolbox.TreeControl(this, this.config_); this.tree_ = tree; tree.setShowRootNode(false); tree.setShowLines(false); @@ -164,18 +212,33 @@ Blockly.Toolbox.prototype.position = function() { var svg = this.workspace_.getParentSvg(); var svgPosition = goog.style.getPageOffset(svg); var svgSize = Blockly.svgSize(svg); - if (this.workspace_.RTL) { - treeDiv.style.left = - (svgPosition.x + svgSize.width - treeDiv.offsetWidth) + 'px'; - } else { + if (this.horizontalLayout_) { treeDiv.style.left = svgPosition.x + 'px'; - } - treeDiv.style.height = svgSize.height + 'px'; - treeDiv.style.top = svgPosition.y + 'px'; - this.width = treeDiv.offsetWidth; - if (!this.workspace_.RTL) { - // For some reason the LTR toolbox now reports as 1px too wide. - this.width -= 1; + treeDiv.style.height = 'auto'; + treeDiv.style.width = svgSize.width + 'px'; + this.height = treeDiv.offsetHeight; + if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { // Top + treeDiv.style.top = svgPosition.y + 'px'; + this.flyout_.setVerticalOffset(treeDiv.offsetHeight); + } else { // Bottom + var topOfToolbox = svgPosition.y + svgSize.height; + treeDiv.style.top = topOfToolbox + 'px'; + this.flyout_.setVerticalOffset(topOfToolbox); + } + } else { + if (this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { // Right + treeDiv.style.left = + (svgPosition.x + svgSize.width - treeDiv.offsetWidth) + 'px'; + } else { // Left + treeDiv.style.left = svgPosition.x + 'px'; + } + treeDiv.style.height = svgSize.height + 'px'; + treeDiv.style.top = svgPosition.y + 'px'; + this.width = treeDiv.offsetWidth; + if (this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { + // For some reason the LTR toolbox now reports as 1px too wide. + this.width -= 1; + } } this.flyout_.position(); }; @@ -187,10 +250,11 @@ Blockly.Toolbox.prototype.position = function() { */ Blockly.Toolbox.prototype.populate_ = function(newTree) { var rootOut = this.tree_; + var that = this; rootOut.removeChildren(); // Delete any existing content. rootOut.blocks = []; var hasColours = false; - function syncTrees(treeIn, treeOut) { + function syncTrees(treeIn, treeOut, pathToMedia) { var lastElement = null; for (var i = 0, childIn; childIn = treeIn.childNodes[i]; i++) { if (!childIn.tagName) { @@ -201,13 +265,17 @@ Blockly.Toolbox.prototype.populate_ = function(newTree) { case 'CATEGORY': var childOut = rootOut.createNode(childIn.getAttribute('name')); childOut.blocks = []; - treeOut.add(childOut); + if (that.horizontalLayout_) { + treeOut.add(childOut); + } else { + treeOut.addChildAt(childOut, 0); + } var custom = childIn.getAttribute('custom'); if (custom) { // Variables and procedures are special dynamic categories. childOut.blocks = custom; } else { - syncTrees(childIn, childOut); + syncTrees(childIn, childOut, pathToMedia); } var colour = childIn.getAttribute('colour'); if (goog.isString(colour)) { @@ -235,7 +303,13 @@ Blockly.Toolbox.prototype.populate_ = function(newTree) { if (lastElement.tagName.toUpperCase() == 'CATEGORY') { // Separator between two categories. // - treeOut.add(new Blockly.Toolbox.TreeSeparator()); + if (that.horizontalLayout_) { + treeOut.add(new Blockly.Toolbox.TreeSeparator( + that.treeSeparatorConfig_)); + } else { + treeOut.addChildAt(new Blockly.Toolbox.TreeSeparator( + that.treeSeparatorConfig_), 0); + } } else { // Change the gap between two blocks. // @@ -259,7 +333,7 @@ Blockly.Toolbox.prototype.populate_ = function(newTree) { } } } - syncTrees(newTree, this.tree_); + syncTrees(newTree, this.tree_, this.workspace_.options.pathToMedia); this.hasColours_ = hasColours; if (rootOut.blocks.length) { @@ -313,16 +387,26 @@ Blockly.Toolbox.prototype.getClientRect = function() { // area are still deleted. Must be smaller than Infinity, but larger than // the largest screen size. var BIG_NUM = 10000000; + var toolboxRect = this.HtmlDiv.getBoundingClientRect(); + + var x = toolboxRect.left; + var y = toolboxRect.top; + var width = toolboxRect.width; + var height = toolboxRect.height; + // Assumes that the toolbox is on the SVG edge. If this changes // (e.g. toolboxes in mutators) then this code will need to be more complex. - var toolboxRect = this.HtmlDiv.getBoundingClientRect(); - if (this.workspace_.RTL) { - var width = toolboxRect.left + toolboxRect.width + BIG_NUM; - return new goog.math.Rect(toolboxRect.left, -BIG_NUM, width, BIG_NUM * 2); + if (this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { + return new goog.math.Rect(-BIG_NUM, -BIG_NUM, BIG_NUM + x + width, + 2 * BIG_NUM); + } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { + return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, 2 * BIG_NUM); + } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { + return new goog.math.Rect(-BIG_NUM, -BIG_NUM, 2 * BIG_NUM, + BIG_NUM + y + height); + } else { // Bottom + return new goog.math.Rect(0, y, 2 * BIG_NUM, BIG_NUM + width); } - // LTR - var width = BIG_NUM + toolboxRect.width + toolboxRect.left; - return new goog.math.Rect(-BIG_NUM, -BIG_NUM, width, BIG_NUM * 2); }; // Extending Closure's Tree UI. @@ -495,18 +579,7 @@ Blockly.Toolbox.TreeNode.prototype.onDoubleClick_ = function(e) { * @constructor * @extends {Blockly.Toolbox.TreeNode} */ -Blockly.Toolbox.TreeSeparator = function() { - Blockly.Toolbox.TreeNode.call(this, null, '', - Blockly.Toolbox.TreeSeparator.CONFIG_); +Blockly.Toolbox.TreeSeparator = function(config) { + Blockly.Toolbox.TreeNode.call(this, null, '', config); }; goog.inherits(Blockly.Toolbox.TreeSeparator, Blockly.Toolbox.TreeNode); - -/** - * Configuration constants for tree separator. - * @type {Object.} - * @const - * @private - */ -Blockly.Toolbox.TreeSeparator.CONFIG_ = { - cssTreeRow: 'blocklyTreeSeparator' -}; diff --git a/core/workspace.js b/core/workspace.js index 47d6ac7c..725e65f3 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -43,6 +43,8 @@ Blockly.Workspace = function(opt_options) { this.options = opt_options || {}; /** @type {boolean} */ this.RTL = !!this.options.RTL; + /** @type {boolean} */ + this.horizontalLayout = !!this.options.horizontalLayout; /** @type {!Array.} */ this.topBlocks_ = []; /** @type {!Array.} */ diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 311126e3..c4b4d046 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -287,7 +287,9 @@ Blockly.WorkspaceSvg.prototype.addFlyout_ = function() { var workspaceOptions = { disabledPatternId: this.options.disabledPatternId, parentWorkspace: this, - RTL: this.RTL + RTL: this.RTL, + horizontalLayout: this.horizontalLayout, + toolboxPosition: this.options.toolboxPosition, }; /** @type {Blockly.Flyout} */ this.flyout_ = new Blockly.Flyout(workspaceOptions); diff --git a/tests/multi_playground.html b/tests/multi_playground.html new file mode 100644 index 00000000..3e2c0db7 --- /dev/null +++ b/tests/multi_playground.html @@ -0,0 +1,462 @@ + + + + +Multi-toolbox Playground + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ LTRRTL
Vertical layout; toolbox at start +
+
+
+
Vertical layout; toolbox at end +
+
+
+
Horizontal layout; toolbox at start +
+
+
+
Horizontal layout; toolbox at end +
+
+
+
+ + + + + + From 9d82bf3a18a5b63f927b25d870cc9ee8e6cc95ed Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Wed, 30 Mar 2016 12:59:29 -0700 Subject: [PATCH 02/32] lint --- core/css.js | 6 ++--- core/flyout.js | 64 ++++++++++++++++++++++--------------------------- core/toolbox.js | 6 ++--- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/core/css.js b/core/css.js index ac416dc7..e9facc8d 100644 --- a/core/css.js +++ b/core/css.js @@ -432,12 +432,12 @@ Blockly.Css.CONTENT = [ '.blocklyHorizontalTree {', 'float: left;', - 'margin: 1px 5px 8px 0px;', + 'margin: 1px 5px 8px 0;', '}', '.blocklyHorizontalTreeRtl {', 'float: right;', - 'margin: 1px 0px 8px 5px;', + 'margin: 1px 0 8px 5px;', '}', '.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {', @@ -450,7 +450,7 @@ Blockly.Css.CONTENT = [ '.blocklyTreeSeparator {', 'border-bottom: solid #e5e5e5 1px;', - 'height: 0px;', + 'height: 0;', 'margin: 5px 0;', '}', diff --git a/core/flyout.js b/core/flyout.js index a665e335..25cc41c8 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -240,7 +240,7 @@ Blockly.Flyout.prototype.getMetrics_ = function() { var optionBox = {height: 0, y: 0}; } - var absoluteTop = this.verticalOffset_ + this.SCROLLBAR_PADDING + var absoluteTop = this.verticalOffset_ + this.SCROLLBAR_PADDING; if (this.horizontalLayout_) { if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { absoluteTop = 0; @@ -284,13 +284,9 @@ Blockly.Flyout.prototype.setMetrics_ = function(xyRatio) { this.workspace_.scrollY = -metrics.contentHeight * xyRatio.y - metrics.contentTop; } else if (this.horizontalLayout_ && goog.isNumber(xyRatio.x)) { - if (this.RTL) { - this.workspace_.scrollX = - -metrics.contentWidth * xyRatio.x + metrics.contentLeft; - } else { - this.workspace_.scrollX = - -metrics.contentWidth * xyRatio.x - metrics.contentLeft; - } + this.workspace_.scrollX = + -metrics.contentWidth * xyRatio.x + + (this.RTL ? metrics.contentLeft : -metrics.contentLeft); } var translateX = this.horizontalLayout_ && this.RTL ? metrics.absoluteLeft + metrics.viewWidth - this.workspace_.scrollX : @@ -382,7 +378,7 @@ Blockly.Flyout.prototype.setBackgroundPath_ = function(width, height) { * @private */ Blockly.Flyout.prototype.setBackgroundPathVertical_ = function(width, height) { - var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT; + var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT; // Decide whether to start on the left or right. var path = ['M ' + (atRight ? this.width_ : 0) + ',0']; // Top. @@ -414,35 +410,35 @@ Blockly.Flyout.prototype.setBackgroundPathVertical_ = function(width, height) { * @private */ Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = function(width, height) { - var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP; - // start at top left. + var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP; + // Start at top left. var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)]; if (atTop) { - // top + // Top. path.push('h', width + this.CORNER_RADIUS); - // right + // Right. path.push('v', height); - // bottom + // Bottom. path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, -this.CORNER_RADIUS, this.CORNER_RADIUS); path.push('h', -1 * (width - this.CORNER_RADIUS)); - // left + // Left. path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, -this.CORNER_RADIUS, -this.CORNER_RADIUS); path.push('z'); } else { - // top + // Top. path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, this.CORNER_RADIUS, -this.CORNER_RADIUS); path.push('h', width - this.CORNER_RADIUS); - // right + // Right. path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, this.CORNER_RADIUS, this.CORNER_RADIUS); path.push('v', height - this.CORNER_RADIUS); - // bottom + // Bottom. path.push('h', -width - this.CORNER_RADIUS); - // left + // Left. path.push('z'); } this.svgBackground_.setAttribute('d', path.join(' ')); @@ -628,7 +624,7 @@ Blockly.Flyout.prototype.show = function(xmlList) { /** * Add listeners to a block that has been added to the flyout. - * @param {Element} root The root node of the SVG group the block is in. + * @param {!Element} root The root node of the SVG group the block is in. * @param {!Blockly.Block} block The block to add listeners for. * @param {!Element} rect The invisible rectangle under the block that acts as * a button for that block. @@ -707,7 +703,7 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) { }; /** - * Handle a mouse-move to vertically drag the flyout. + * Handle a mouse-move to drag the flyout. * @param {!Event} e Mouse move event. * @private */ @@ -717,16 +713,14 @@ Blockly.Flyout.prototype.onMouseMove_ = function(e) { this.startDragMouseX_ = e.clientX; var metrics = this.getMetrics_(); var x = metrics.viewLeft - dx; - x = Math.min(x, metrics.contentWidth - metrics.viewWidth); - x = Math.max(x, 0); + x = goog.math.clamp(x, 0, metrics.contentWidth - metrics.viewWidth); this.scrollbar_.set(x); } else { var dy = e.clientY - this.startDragMouseY_; this.startDragMouseY_ = e.clientY; var metrics = this.getMetrics_(); var y = metrics.viewTop - dy; - y = Math.min(y, metrics.contentHeight - metrics.viewHeight); - y = Math.max(y, 0); + y = goog.math.clamp(y, o, metrics.contentHeight - metrics.viewHeight); this.scrollbar_.set(y); } }; @@ -859,16 +853,15 @@ Blockly.Flyout.prototype.getClientRect = function() { if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) { return new goog.math.Rect(-BIG_NUM, y - BIG_NUM, BIG_NUM * 2, - BIG_NUM + height); + BIG_NUM + height); } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { return new goog.math.Rect(-BIG_NUM, y + this.verticalOffset_, BIG_NUM * 2, - BIG_NUM + height); + BIG_NUM + height); } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { return new goog.math.Rect(x - BIG_NUM, -BIG_NUM, BIG_NUM + width, - BIG_NUM * 2); + BIG_NUM * 2); } else { // Right - return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, - BIG_NUM * 2); + return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, BIG_NUM * 2); } }; @@ -907,14 +900,13 @@ Blockly.Flyout.prototype.reflowHorizontal = function() { var flyoutHeight = 0; var margin = this.CORNER_RADIUS; var blocks = this.workspace_.getTopBlocks(false); - for (var x = 0, block; block = blocks[x]; x++) { - var height = block.getHeightWidth().height; - flyoutHeight = Math.max(flyoutHeight, height); + for (var i = 0, block; block = blocks[i]; i++) { + flyoutHeight = Math.max(flyoutHeight, block.getHeightWidth().height); } flyoutHeight *= this.workspace_.scale; flyoutHeight += margin * 1.5 + Blockly.Scrollbar.scrollbarThickness; if (this.height_ != flyoutHeight) { - for (var x = 0, block; block = blocks[x]; x++) { + for (var i = 0, block; block = blocks[i]; i++) { var blockHW = block.getHeightWidth(); if (block.flyoutRect_) { block.flyoutRect_.setAttribute('width', blockHW.width); @@ -941,7 +933,7 @@ Blockly.Flyout.prototype.reflowVertical = function() { var flyoutWidth = 0; var margin = this.CORNER_RADIUS; var blocks = this.workspace_.getTopBlocks(false); - for (var x = 0, block; block = blocks[x]; x++) { + for (var i = 0, block; block = blocks[i]; i++) { var width = block.getHeightWidth().width; if (block.outputConnection) { width -= Blockly.BlockSvg.TAB_WIDTH; @@ -952,7 +944,7 @@ Blockly.Flyout.prototype.reflowVertical = function() { flyoutWidth *= this.workspace_.scale; flyoutWidth += margin * 1.5 + Blockly.Scrollbar.scrollbarThickness; if (this.width_ != flyoutWidth) { - for (var x = 0, block; block = blocks[x]; x++) { + for (var i = 0, block; block = blocks[i]; i++) { var blockHW = block.getHeightWidth(); if (this.RTL) { // With the flyoutWidth known, right-align the blocks. diff --git a/core/toolbox.js b/core/toolbox.js index c3478a0b..568b98b6 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -402,10 +402,10 @@ Blockly.Toolbox.prototype.getClientRect = function() { } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, 2 * BIG_NUM); } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { - return new goog.math.Rect(-BIG_NUM, -BIG_NUM, 2 * BIG_NUM, - BIG_NUM + y + height); + return new goog.math.Rect(-BIG_NUM, -BIG_NUM, 2 * BIG_NUM, + BIG_NUM + y + height); } else { // Bottom - return new goog.math.Rect(0, y, 2 * BIG_NUM, BIG_NUM + width); + return new goog.math.Rect(0, y, 2 * BIG_NUM, BIG_NUM + width); } }; From a4bdede170aec22703ac490695ceb8b67f271ea5 Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Thu, 31 Mar 2016 13:05:21 -0700 Subject: [PATCH 03/32] Fix scrollbar positioning and category ordering --- core/css.js | 2 +- core/flyout.js | 8 +++++++- core/toolbox.js | 15 +++------------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/core/css.js b/core/css.js index e9facc8d..6c09b942 100644 --- a/core/css.js +++ b/core/css.js @@ -456,7 +456,7 @@ Blockly.Css.CONTENT = [ '.blocklyTreeSeparatorHorizontal {', 'border-right: solid #e5e5e5 1px;', - 'width: 0px;', + 'width: 0;', 'padding: 5px 0;', 'margin: 0 5px;', '}', diff --git a/core/flyout.js b/core/flyout.js index 25cc41c8..466b4060 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -246,10 +246,16 @@ Blockly.Flyout.prototype.getMetrics_ = function() { absoluteTop = 0; } var viewHeight = this.height_; + if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) { + viewHeight += this.CORNER_RADIUS - this.SCROLLBAR_PADDING; + } var viewWidth = this.width_ - 2 * this.SCROLLBAR_PADDING; } else { var viewHeight = this.height_ - 2 * this.SCROLLBAR_PADDING; var viewWidth = this.width_; + if (!this.RTL) { + viewWidth -= this.SCROLLBAR_PADDING; + } } var metrics = { @@ -720,7 +726,7 @@ Blockly.Flyout.prototype.onMouseMove_ = function(e) { this.startDragMouseY_ = e.clientY; var metrics = this.getMetrics_(); var y = metrics.viewTop - dy; - y = goog.math.clamp(y, o, metrics.contentHeight - metrics.viewHeight); + y = goog.math.clamp(y, 0, metrics.contentHeight - metrics.viewHeight); this.scrollbar_.set(y); } }; diff --git a/core/toolbox.js b/core/toolbox.js index 568b98b6..26fd596e 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -265,11 +265,7 @@ Blockly.Toolbox.prototype.populate_ = function(newTree) { case 'CATEGORY': var childOut = rootOut.createNode(childIn.getAttribute('name')); childOut.blocks = []; - if (that.horizontalLayout_) { - treeOut.add(childOut); - } else { - treeOut.addChildAt(childOut, 0); - } + treeOut.add(childOut); var custom = childIn.getAttribute('custom'); if (custom) { // Variables and procedures are special dynamic categories. @@ -303,13 +299,8 @@ Blockly.Toolbox.prototype.populate_ = function(newTree) { if (lastElement.tagName.toUpperCase() == 'CATEGORY') { // Separator between two categories. // - if (that.horizontalLayout_) { - treeOut.add(new Blockly.Toolbox.TreeSeparator( - that.treeSeparatorConfig_)); - } else { - treeOut.addChildAt(new Blockly.Toolbox.TreeSeparator( - that.treeSeparatorConfig_), 0); - } + treeOut.add(new Blockly.Toolbox.TreeSeparator( + that.treeSeparatorConfig_)); } else { // Change the gap between two blocks. // From 6202113237f3f83508b37a90b828e13773f5cc8e Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Fri, 8 Apr 2016 15:34:08 -0700 Subject: [PATCH 04/32] fix accidental flyout scrolling --- core/flyout.js | 109 ++++++++++++++++++++++++++++++------------------ core/toolbox.js | 8 ++-- 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index 466b4060..b8479349 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -59,12 +59,14 @@ Blockly.Flyout = function(workspaceOptions) { /** * Flyout should be laid out horizontally vs vertically. * @type {boolean} + * @private */ this.horizontalLayout_ = workspaceOptions.horizontalLayout; /** * Position of the toolbox and flyout relative to the workspace. * @type {number} + * @private */ this.toolboxPosition_ = workspaceOptions.toolboxPosition; @@ -303,7 +305,7 @@ Blockly.Flyout.prototype.setMetrics_ = function(xyRatio) { Blockly.Flyout.prototype.setVerticalOffset = function(verticalOffset) { this.verticalOffset_ = verticalOffset; -} +}; /** * Move the toolbox to the edge of the workspace. @@ -324,7 +326,8 @@ Blockly.Flyout.prototype.position = function() { } this.setBackgroundPath_(edgeWidth, - this.horizontalLayout_ ? this.height_ + this.verticalOffset_ : metrics.viewHeight); + this.horizontalLayout_ ? + this.height_ + this.verticalOffset_ : metrics.viewHeight); var x = metrics.absoluteLeft; if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { @@ -376,7 +379,8 @@ Blockly.Flyout.prototype.setBackgroundPath_ = function(width, height) { }; /** - * Create and set the path for the visible boundaries of the toolbox in vertical mode. + * Create and set the path for the visible boundaries of the toolbox in vertical + * mode. * @param {number} width The width of the toolbox, not including the * rounded corners. * @param {number} height The height of the toolbox, not including @@ -408,14 +412,16 @@ Blockly.Flyout.prototype.setBackgroundPathVertical_ = function(width, height) { }; /** - * Create and set the path for the visible boundaries of the toolbox in horizontal mode. + * Create and set the path for the visible boundaries of the toolbox in + * horizontal mode. * @param {number} width The width of the toolbox, not including the * rounded corners. * @param {number} height The height of the toolbox, not including * rounded corners. * @private */ -Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = function(width, height) { +Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = + function(width, height) { var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP; // Start at top left. var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)]; @@ -454,7 +460,7 @@ Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = function(width, height) * Scroll the flyout to the top. */ Blockly.Flyout.prototype.scrollToStart = function() { - this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? 1000000000 : 0); + this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? Infinity : 0); }; /** @@ -584,7 +590,8 @@ Blockly.Flyout.prototype.show = function(xmlList) { block.render(); var root = block.getSvgRoot(); var blockHW = block.getHeightWidth(); - block.moveBy((this.horizontalLayout_ && this.RTL) ? -cursorX : cursorX, cursorY); + block.moveBy((this.horizontalLayout_ && this.RTL) ? + -cursorX : cursorX, cursorY); if (this.horizontalLayout_) { cursorX += blockHW.width + gaps[i]; } else { @@ -699,6 +706,7 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) { Blockly.hideChaff(true); Blockly.Flyout.terminateDrag_(); this.startDragMouseY_ = e.clientY; + this.startDragMouseX_ = e.clientX; Blockly.Flyout.onMouseMoveWrapper_ = Blockly.bindEvent_(document, 'mousemove', this, this.onMouseMove_); Blockly.Flyout.onMouseUpWrapper_ = Blockly.bindEvent_(document, 'mouseup', @@ -714,17 +722,22 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) { * @private */ Blockly.Flyout.prototype.onMouseMove_ = function(e) { + var metrics = this.getMetrics_(); if (this.horizontalLayout_) { + if (metrics.contentWidth - metrics.viewWidth < 0) { + return; + } var dx = e.clientX - this.startDragMouseX_; this.startDragMouseX_ = e.clientX; - var metrics = this.getMetrics_(); var x = metrics.viewLeft - dx; x = goog.math.clamp(x, 0, metrics.contentWidth - metrics.viewWidth); this.scrollbar_.set(x); } else { + if (metrics.contentHeight - metrics.viewHeight < 0) { + return; + } var dy = e.clientY - this.startDragMouseY_; this.startDragMouseY_ = e.clientY; - var metrics = this.getMetrics_(); var y = metrics.viewTop - dy; y = goog.math.clamp(y, 0, metrics.contentHeight - metrics.viewHeight); this.scrollbar_.set(y); @@ -778,37 +791,7 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { return; } Blockly.Events.disable(); - // Create the new block by cloning the block in the flyout (via XML). - var xml = Blockly.Xml.blockToDom(originBlock); - var block = Blockly.Xml.domToBlock(xml, workspace); - // Place it in the same spot as the flyout copy. - var svgRootOld = originBlock.getSvgRoot(); - if (!svgRootOld) { - throw 'originBlock is not rendered.'; - } - var xyOld = Blockly.getSvgXY_(svgRootOld, workspace); - // Scale the scroll (getSvgXY_ did not do this). - if (flyout.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { - var width = workspace.getMetrics().viewWidth - flyout.width_; - xyOld.x += width / workspace.scale - width; - } else { - xyOld.x += flyout.workspace_.scrollX / flyout.workspace_.scale - - flyout.workspace_.scrollX; - } - xyOld.y += flyout.workspace_.scrollY / flyout.workspace_.scale - - flyout.workspace_.scrollY; - var svgRootNew = block.getSvgRoot(); - if (!svgRootNew) { - throw 'block is not rendered.'; - } - var xyNew = Blockly.getSvgXY_(svgRootNew, workspace); - // Scale the scroll (getSvgXY_ did not do this). - xyNew.x += workspace.scrollX / workspace.scale - workspace.scrollX; - xyNew.y += workspace.scrollY / workspace.scale - workspace.scrollY; - if (workspace.toolbox_ && !workspace.scrollbar) { - xyNew.x += workspace.toolbox_.width / workspace.scale; - } - block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y); + var block = Blockly.Flyout.placeNewBlock_(originBlock, workspace, flyout); Blockly.Events.enable(); if (Blockly.Events.isEnabled()) { Blockly.Events.setGroup(true); @@ -826,6 +809,50 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { }; }; +Blockly.Flyout.placeNewBlock_ = function(originBlock, workspace, + flyout) { + var svgRootOld = originBlock.getSvgRoot(); + if (!svgRootOld) { + throw 'originBlock is not rendered.'; + } + // Figure out where the original block is on the screen, relative to the upper + // left corner of the workspace. + var xyOld = Blockly.getSvgXY_(svgRootOld, workspace); + // Scale the scroll (getSvgXY_ did not do this). + if (flyout.RTL) { + var width = workspace.getMetrics().viewWidth - flyout.width_; + xyOld.x += width / workspace.scale - width; + } else { + xyOld.x += flyout.workspace_.scrollX / flyout.workspace_.scale - + flyout.workspace_.scrollX; + } + xyOld.y += flyout.workspace_.scrollY / flyout.workspace_.scale - + flyout.workspace_.scrollY; + + // Create the new block by cloning the block in the flyout (via XML). + var xml = Blockly.Xml.blockToDom(originBlock); + var block = Blockly.Xml.domToBlock(xml, workspace); + // Place it in the same spot as the flyout copy. + var svgRootNew = block.getSvgRoot(); + if (!svgRootNew) { + throw 'block is not rendered.'; + } + // Figure out where the new block got placed on the screen, relative to the + // upper left corner of the workspace. This may not be the same as the + // original block because the original block was in the flyout. + var xyNew = Blockly.getSvgXY_(svgRootNew, workspace); + // Scale the scroll (getSvgXY_ did not do this). + xyNew.x += workspace.scrollX / workspace.scale - workspace.scrollX; + xyNew.y += workspace.scrollY / workspace.scale - workspace.scrollY; + if (workspace.toolbox_ && !workspace.scrollbar) { + xyNew.x += workspace.toolbox_.width / workspace.scale; + xyNew.y += workspace.toolbox_.height / workspace.scale; + } + // Move the new block to where the old block is. + block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y); + return block; +}; + /** * Filter the blocks on the flyout to disable the ones that are above the * capacity limit. @@ -984,4 +1011,4 @@ Blockly.Flyout.prototype.reflow = function() { } // Fire a resize event to update the flyout's scrollbar. Blockly.fireUiEvent(window, 'resize'); -}; \ No newline at end of file +}; diff --git a/core/toolbox.js b/core/toolbox.js index 26fd596e..df29e5c6 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -102,11 +102,13 @@ Blockly.Toolbox = function(workspace) { if (this.horizontalLayout_) { this.config_['cssTreeRow'] = this.config_['cssTreeRow'] + - (workspace.RTL ? ' blocklyHorizontalTreeRtl' : ' blocklyHorizontalTree'); + (workspace.RTL ? + ' blocklyHorizontalTreeRtl' : ' blocklyHorizontalTree'); this.treeSeparatorConfig_['cssTreeRow'] = 'blocklyTreeSeparatorHorizontal' + - (workspace.RTL ? ' blocklyHorizontalTreeRtl' : ' blocklyHorizontalTree'); + (workspace.RTL ? + ' blocklyHorizontalTreeRtl' : ' blocklyHorizontalTree'); this.config_['cssTreeIcon'] = ''; } }; @@ -396,7 +398,7 @@ Blockly.Toolbox.prototype.getClientRect = function() { return new goog.math.Rect(-BIG_NUM, -BIG_NUM, 2 * BIG_NUM, BIG_NUM + y + height); } else { // Bottom - return new goog.math.Rect(0, y, 2 * BIG_NUM, BIG_NUM + width); + return new goog.math.Rect(0, y, 2 * BIG_NUM, BIG_NUM + width); } }; From 1e3297ba0aaeaae9ad8792c984a24d5275edfab1 Mon Sep 17 00:00:00 2001 From: miguel76 Date: Sat, 9 Apr 2016 12:01:38 -0300 Subject: [PATCH 05/32] Fix #326 Parameter block is removed from init prototype of fields and its usage is replaced by this.sourceBlock_ --- core/field_checkbox.js | 5 ++--- core/field_colour.js | 5 ++--- core/field_dropdown.js | 7 +++---- core/field_variable.js | 9 +++++---- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/core/field_checkbox.js b/core/field_checkbox.js index 183e226c..fb46e0a4 100644 --- a/core/field_checkbox.js +++ b/core/field_checkbox.js @@ -58,14 +58,13 @@ Blockly.FieldCheckbox.prototype.CURSOR = 'default'; /** * Install this checkbox on a block. - * @param {!Blockly.Block} block The block containing this text. */ -Blockly.FieldCheckbox.prototype.init = function(block) { +Blockly.FieldCheckbox.prototype.init = function() { if (this.fieldGroup_) { // Checkbox has already been initialized once. return; } - Blockly.FieldCheckbox.superClass_.init.call(this, block); + Blockly.FieldCheckbox.superClass_.init.call(this); // The checkbox doesn't use the inherited text element. // Instead it uses a custom checkmark element that is either visible or not. this.checkElement_ = Blockly.createSvgElement('text', diff --git a/core/field_colour.js b/core/field_colour.js index ee75f473..65346ee2 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -66,10 +66,9 @@ Blockly.FieldColour.prototype.columns_ = 0; /** * Install this field on a block. - * @param {!Blockly.Block} block The block containing this field. */ -Blockly.FieldColour.prototype.init = function(block) { - Blockly.FieldColour.superClass_.init.call(this, block); +Blockly.FieldColour.prototype.init = function() { + Blockly.FieldColour.superClass_.init.call(this); this.borderRect_.style['fillOpacity'] = 1; this.setValue(this.getValue()); }; diff --git a/core/field_dropdown.js b/core/field_dropdown.js index d715c8c7..294682fc 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -77,9 +77,8 @@ Blockly.FieldDropdown.prototype.CURSOR = 'default'; /** * Install this dropdown on a block. - * @param {!Blockly.Block} block The block containing this text. */ -Blockly.FieldDropdown.prototype.init = function(block) { +Blockly.FieldDropdown.prototype.init = function() { if (this.fieldGroup_) { // Dropdown has already been initialized once. return; @@ -87,10 +86,10 @@ Blockly.FieldDropdown.prototype.init = function(block) { // Add dropdown arrow: "option ▾" (LTR) or "▾ אופציה" (RTL) this.arrow_ = Blockly.createSvgElement('tspan', {}, null); this.arrow_.appendChild(document.createTextNode( - block.RTL ? Blockly.FieldDropdown.ARROW_CHAR + ' ' : + this.sourceBlock_.RTL ? Blockly.FieldDropdown.ARROW_CHAR + ' ' : ' ' + Blockly.FieldDropdown.ARROW_CHAR)); - Blockly.FieldDropdown.superClass_.init.call(this, block); + Blockly.FieldDropdown.superClass_.init.call(this); // Force a reset of the text to add the arrow. var text = this.text_; this.text_ = null; diff --git a/core/field_variable.js b/core/field_variable.js index 8e5bac96..0a040a0a 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -79,18 +79,19 @@ Blockly.FieldVariable.prototype.setValidator = function(handler) { /** * Install this dropdown on a block. - * @param {!Blockly.Block} block The block containing this text. */ -Blockly.FieldVariable.prototype.init = function(block) { +Blockly.FieldVariable.prototype.init = function() { if (this.fieldGroup_) { // Dropdown has already been initialized once. return; } - Blockly.FieldVariable.superClass_.init.call(this, block); + Blockly.FieldVariable.superClass_.init.call(this); if (!this.getValue()) { // Variables without names get uniquely named for this workspace. var workspace = - block.isInFlyout ? block.workspace.targetWorkspace : block.workspace; + this.sourceBlock_.isInFlyout ? + this.sourceBlock_.workspace.targetWorkspace : + this.sourceBlock_.workspace; this.setValue(Blockly.Variables.generateUniqueName(workspace)); } }; From 3af4a6848af0beceec3d48c498e186b90e72d74f Mon Sep 17 00:00:00 2001 From: miguel76 Date: Sat, 9 Apr 2016 12:18:27 -0300 Subject: [PATCH 06/32] Fix inconsistent behaviour --- core/input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/input.js b/core/input.js index 8ca26558..5b3a46fa 100644 --- a/core/input.js +++ b/core/input.js @@ -222,7 +222,7 @@ Blockly.Input.prototype.init = function() { return; // Headless blocks don't need fields initialized. } for (var x = 0; x < this.fieldRow.length; x++) { - this.fieldRow[x].init(this.sourceBlock_); + this.fieldRow[x].init(); } }; From 7cf5de5774cc1e47b225ad17ceb2ed14948cd30e Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Fri, 8 Apr 2016 17:16:44 -0700 Subject: [PATCH 07/32] Dragging out of flyout with zoom works for horizontal flyouts in LTR mode --- core/flyout.js | 55 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index b8479349..75ad75d3 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -809,6 +809,14 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { }; }; +/** + * Copy a block from the flyout to the workspace and position it correctly. + * @param {!Blockly.Block} originBlock The flyout block to copy. + * @param {!Blockly.Workspace} workspace The main workspace. + * @param {!Blockly.Flyout} flyout The flyout where originBlock originates. + * @return {!Blockly.Block} The new block in the main workspace. + * @private + */ Blockly.Flyout.placeNewBlock_ = function(originBlock, workspace, flyout) { var svgRootOld = originBlock.getSvgRoot(); @@ -816,38 +824,61 @@ Blockly.Flyout.placeNewBlock_ = function(originBlock, workspace, throw 'originBlock is not rendered.'; } // Figure out where the original block is on the screen, relative to the upper - // left corner of the workspace. + // left corner of the main workspace. var xyOld = Blockly.getSvgXY_(svgRootOld, workspace); - // Scale the scroll (getSvgXY_ did not do this). - if (flyout.RTL) { - var width = workspace.getMetrics().viewWidth - flyout.width_; - xyOld.x += width / workspace.scale - width; - } else { - xyOld.x += flyout.workspace_.scrollX / flyout.workspace_.scale - - flyout.workspace_.scrollX; + // If the flyout is on the right side, (0, 0) in the flyout is offset to + // the right of (0, 0) in the main workspace. Offset to take that into + // account. + if (flyout.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { + var scrollX = workspace.getMetrics().viewWidth - flyout.width_; + var scale = workspace.scale; + // Scale the scroll (getSvgXY_ did not do this). + xyOld.x += scrollX / scale - scrollX; } - xyOld.y += flyout.workspace_.scrollY / flyout.workspace_.scale - - flyout.workspace_.scrollY; + // Take into account that the flyout might have been scrolled horizontally + // (separately from the main workspace). + // Generally a no-op in vertical mode but likely to happen in horizontal + // mode. + scrollX = flyout.workspace_.scrollX; + scale = flyout.workspace_.scale; + xyOld.x += scrollX / scale - scrollX; + + // If the flyout is on the bottom, (0, 0) in the flyout is offset to be below + // (0, 0) in the main workspace. Offset to take that into account. + if (flyout.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { + var scrollY = workspace.getMetrics().viewHeight - flyout.height_; + var scale = workspace.scale; + xyOld.y += scrollY / scale - scrollY; + } + // Take into account that the flyout might have been scrolled vertically + // (separately from the main workspace). + // Generally a no-op in horizontal mode but likely to happen in vertical + // mode. + scrollY = flyout.workspace_.scrollY; + scale = flyout.workspace_.scale; + xyOld.y += scrollY / scale - scrollY; // Create the new block by cloning the block in the flyout (via XML). var xml = Blockly.Xml.blockToDom(originBlock); var block = Blockly.Xml.domToBlock(xml, workspace); - // Place it in the same spot as the flyout copy. var svgRootNew = block.getSvgRoot(); if (!svgRootNew) { throw 'block is not rendered.'; } // Figure out where the new block got placed on the screen, relative to the // upper left corner of the workspace. This may not be the same as the - // original block because the original block was in the flyout. + // original block because the flyout's origin may not be the same as the + // main workspace's origin. var xyNew = Blockly.getSvgXY_(svgRootNew, workspace); // Scale the scroll (getSvgXY_ did not do this). xyNew.x += workspace.scrollX / workspace.scale - workspace.scrollX; xyNew.y += workspace.scrollY / workspace.scale - workspace.scrollY; + // If the flyout is collapsible and the workspace can't be scrolled if (workspace.toolbox_ && !workspace.scrollbar) { xyNew.x += workspace.toolbox_.width / workspace.scale; xyNew.y += workspace.toolbox_.height / workspace.scale; } + // Move the new block to where the old block is. block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y); return block; From 01955c6615ce33bfa12e96e52674ddc4964a08ac Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Tue, 12 Apr 2016 13:14:49 -0700 Subject: [PATCH 08/32] lint --- core/flyout.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index 75ad75d3..bd92ae83 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -460,7 +460,7 @@ Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = * Scroll the flyout to the top. */ Blockly.Flyout.prototype.scrollToStart = function() { - this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? Infinity : 0); + this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? 1000000000 : 0); }; /** @@ -826,37 +826,37 @@ Blockly.Flyout.placeNewBlock_ = function(originBlock, workspace, // Figure out where the original block is on the screen, relative to the upper // left corner of the main workspace. var xyOld = Blockly.getSvgXY_(svgRootOld, workspace); - // If the flyout is on the right side, (0, 0) in the flyout is offset to - // the right of (0, 0) in the main workspace. Offset to take that into - // account. - if (flyout.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { - var scrollX = workspace.getMetrics().viewWidth - flyout.width_; - var scale = workspace.scale; - // Scale the scroll (getSvgXY_ did not do this). - xyOld.x += scrollX / scale - scrollX; - } // Take into account that the flyout might have been scrolled horizontally // (separately from the main workspace). // Generally a no-op in vertical mode but likely to happen in horizontal // mode. - scrollX = flyout.workspace_.scrollX; - scale = flyout.workspace_.scale; + var scrollX = flyout.workspace_.scrollX; + var scale = flyout.workspace_.scale; xyOld.x += scrollX / scale - scrollX; - - // If the flyout is on the bottom, (0, 0) in the flyout is offset to be below - // (0, 0) in the main workspace. Offset to take that into account. - if (flyout.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { - var scrollY = workspace.getMetrics().viewHeight - flyout.height_; - var scale = workspace.scale; - xyOld.y += scrollY / scale - scrollY; + // If the flyout is on the right side, (0, 0) in the flyout is offset to + // the right of (0, 0) in the main workspace. Offset to take that into + // account. + if (flyout.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { + scrollX = workspace.getMetrics().viewWidth - flyout.width_; + scale = workspace.scale; + // Scale the scroll (getSvgXY_ did not do this). + xyOld.x += scrollX / scale - scrollX; } + // Take into account that the flyout might have been scrolled vertically // (separately from the main workspace). // Generally a no-op in horizontal mode but likely to happen in vertical // mode. - scrollY = flyout.workspace_.scrollY; + var scrollY = flyout.workspace_.scrollY; scale = flyout.workspace_.scale; xyOld.y += scrollY / scale - scrollY; + // If the flyout is on the bottom, (0, 0) in the flyout is offset to be below + // (0, 0) in the main workspace. Offset to take that into account. + if (flyout.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { + scrollY = workspace.getMetrics().viewHeight - flyout.height_; + scale = workspace.scale; + xyOld.y += scrollY / scale - scrollY; + } // Create the new block by cloning the block in the flyout (via XML). var xml = Blockly.Xml.blockToDom(originBlock); @@ -873,7 +873,7 @@ Blockly.Flyout.placeNewBlock_ = function(originBlock, workspace, // Scale the scroll (getSvgXY_ did not do this). xyNew.x += workspace.scrollX / workspace.scale - workspace.scrollX; xyNew.y += workspace.scrollY / workspace.scale - workspace.scrollY; - // If the flyout is collapsible and the workspace can't be scrolled + // If the flyout is collapsible and the workspace can't be scrolled. if (workspace.toolbox_ && !workspace.scrollbar) { xyNew.x += workspace.toolbox_.width / workspace.scale; xyNew.y += workspace.toolbox_.height / workspace.scale; From 90b1cce5d59599d86ae7428a95fb8bfae93677e3 Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Wed, 13 Apr 2016 13:56:09 -0700 Subject: [PATCH 09/32] Correctly locate and size scrollbar for all toolbox positions. --- core/blockly.js | 15 ++++- core/scrollbar.js | 136 +++++++++++++++++++++++++++------------------- 2 files changed, 92 insertions(+), 59 deletions(-) diff --git a/core/blockly.js b/core/blockly.js index b7adeb07..f3f99766 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -413,7 +413,13 @@ Blockly.hideChaff = function(opt_allowToolbox) { Blockly.getMainWorkspaceMetrics_ = function() { var svgSize = Blockly.svgSize(this.getParentSvg()); if (this.toolbox_) { - svgSize.width -= this.toolbox_.width; + // If the toolbox is at the bottom it's laid out separately from the main + // workspace, rather than overlapping. + if (this.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { + svgSize.height -= this.toolbox_.height; + } else { + svgSize.width -= this.toolbox_.width; + } } // Set the margin to match the flyout's margin so that the workspace does // not jump as blocks are added. @@ -448,6 +454,11 @@ Blockly.getMainWorkspaceMetrics_ = function() { if (this.toolbox_ && this.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { absoluteLeft = this.toolbox_.width; } + var absoluteTop = 0; + if (this.toolbox_ && this.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { + absoluteTop = this.toolbox_.height; + } + var metrics = { viewHeight: svgSize.height, viewWidth: svgSize.width, @@ -457,7 +468,7 @@ Blockly.getMainWorkspaceMetrics_ = function() { viewLeft: -this.scrollX, contentTop: topEdge, contentLeft: leftEdge, - absoluteTop: 0, + absoluteTop: absoluteTop, absoluteLeft: absoluteLeft }; return metrics; diff --git a/core/scrollbar.js b/core/scrollbar.js index 42d053cc..b8534d75 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -270,69 +270,91 @@ Blockly.Scrollbar.prototype.resize = function(opt_metrics) { * .absoluteLeft: Left-edge of view. */ if (this.horizontal_) { - var outerLength = hostMetrics.viewWidth - 1; - if (this.pair_) { - // Shorten the scrollbar to make room for the corner square. - outerLength -= Blockly.Scrollbar.scrollbarThickness; - } else { - // Only show the scrollbar if needed. - // Ideally this would also apply to scrollbar pairs, but that's a bigger - // headache (due to interactions with the corner square). - this.setVisible(outerLength < hostMetrics.contentWidth); - } - this.ratio_ = outerLength / hostMetrics.contentWidth; - if (this.ratio_ === -Infinity || this.ratio_ === Infinity || - isNaN(this.ratio_)) { - this.ratio_ = 0; - } - var innerLength = hostMetrics.viewWidth * this.ratio_; - var innerOffset = (hostMetrics.viewLeft - hostMetrics.contentLeft) * - this.ratio_; - this.svgKnob_.setAttribute('width', Math.max(0, innerLength)); - this.xCoordinate = hostMetrics.absoluteLeft + 0.5; - if (this.pair_ && this.workspace_.RTL) { - this.xCoordinate += hostMetrics.absoluteLeft + - Blockly.Scrollbar.scrollbarThickness; - } - this.yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight - - Blockly.Scrollbar.scrollbarThickness - 0.5; - this.svgGroup_.setAttribute('transform', - 'translate(' + this.xCoordinate + ',' + this.yCoordinate + ')'); - this.svgBackground_.setAttribute('width', Math.max(0, outerLength)); - this.svgKnob_.setAttribute('x', this.constrainKnob_(innerOffset)); + this.resizeHorizontal_(hostMetrics); } else { - var outerLength = hostMetrics.viewHeight - 1; - if (this.pair_) { - // Shorten the scrollbar to make room for the corner square. - outerLength -= Blockly.Scrollbar.scrollbarThickness; - } else { - // Only show the scrollbar if needed. - this.setVisible(outerLength < hostMetrics.contentHeight); - } - this.ratio_ = outerLength / hostMetrics.contentHeight; - if (this.ratio_ === -Infinity || this.ratio_ === Infinity || - isNaN(this.ratio_)) { - this.ratio_ = 0; - } - var innerLength = hostMetrics.viewHeight * this.ratio_; - var innerOffset = (hostMetrics.viewTop - hostMetrics.contentTop) * - this.ratio_; - this.svgKnob_.setAttribute('height', Math.max(0, innerLength)); - this.xCoordinate = hostMetrics.absoluteLeft + 0.5; - if (!this.workspace_.RTL) { - this.xCoordinate += hostMetrics.viewWidth - - Blockly.Scrollbar.scrollbarThickness - 1; - } - this.yCoordinate = hostMetrics.absoluteTop + 0.5; - this.svgGroup_.setAttribute('transform', - 'translate(' + this.xCoordinate + ',' + this.yCoordinate + ')'); - this.svgBackground_.setAttribute('height', Math.max(0, outerLength)); - this.svgKnob_.setAttribute('y', this.constrainKnob_(innerOffset)); + this.resizeVertical_(hostMetrics); } // Resizing may have caused some scrolling. this.onScroll_(); }; +/** + * Recalculate a horizontal scrollbar's location and length. + * @param {!Object} hostMetrics A data structure describing all the + * required dimensions, possibly fetched from the host + * object. + * @private + */ +Blockly.Scrollbar.prototype.resizeHorizontal_ = function(hostMetrics) { + var outerLength = hostMetrics.viewWidth - 1; + if (this.pair_) { + // Shorten the scrollbar to make room for the corner square. + outerLength -= Blockly.Scrollbar.scrollbarThickness; + } else { + // Only show the scrollbar if needed. + // Ideally this would also apply to scrollbar pairs, but that's a bigger + // headache (due to interactions with the corner square). + this.setVisible(outerLength < hostMetrics.contentWidth); + } + this.ratio_ = outerLength / hostMetrics.contentWidth; + if (this.ratio_ === -Infinity || this.ratio_ === Infinity || + isNaN(this.ratio_)) { + this.ratio_ = 0; + } + var innerLength = hostMetrics.viewWidth * this.ratio_; + var innerOffset = (hostMetrics.viewLeft - hostMetrics.contentLeft) * + this.ratio_; + this.svgKnob_.setAttribute('width', Math.max(0, innerLength)); + this.xCoordinate = hostMetrics.absoluteLeft + 0.5; + if (this.pair_ && this.workspace_.RTL) { + this.xCoordinate += Blockly.Scrollbar.scrollbarThickness; + } + // Horizontal toolbar should always be just above the bottom of the workspace. + this.yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight - + Blockly.Scrollbar.scrollbarThickness - 0.5; + this.svgGroup_.setAttribute('transform', + 'translate(' + this.xCoordinate + ',' + this.yCoordinate + ')'); + this.svgBackground_.setAttribute('width', Math.max(0, outerLength)); + this.svgKnob_.setAttribute('x', this.constrainKnob_(innerOffset)); +}; + +/** + * Recalculate a vertical scrollbar's location and length. + * @param {!Object} hostMetrics A data structure describing all the + * required dimensions, possibly fetched from the host + * object. + * @private + */ +Blockly.Scrollbar.prototype.resizeVertical_ = function(hostMetrics) { + var outerLength = hostMetrics.viewHeight - 1; + if (this.pair_) { + // Shorten the scrollbar to make room for the corner square. + outerLength -= Blockly.Scrollbar.scrollbarThickness; + } else { + // Only show the scrollbar if needed. + this.setVisible(outerLength < hostMetrics.contentHeight); + } + this.ratio_ = outerLength / hostMetrics.contentHeight; + if (this.ratio_ === -Infinity || this.ratio_ === Infinity || + isNaN(this.ratio_)) { + this.ratio_ = 0; + } + var innerLength = hostMetrics.viewHeight * this.ratio_; + var innerOffset = (hostMetrics.viewTop - hostMetrics.contentTop) * + this.ratio_; + this.svgKnob_.setAttribute('height', Math.max(0, innerLength)); + this.xCoordinate = hostMetrics.absoluteLeft + 0.5; + if (!this.workspace_.RTL) { + this.xCoordinate += hostMetrics.viewWidth - + Blockly.Scrollbar.scrollbarThickness - 1; + } + this.yCoordinate = hostMetrics.absoluteTop + 0.5; + this.svgGroup_.setAttribute('transform', + 'translate(' + this.xCoordinate + ',' + this.yCoordinate + ')'); + this.svgBackground_.setAttribute('height', Math.max(0, outerLength)); + this.svgKnob_.setAttribute('y', this.constrainKnob_(innerOffset)); +}; + /** * Create all the DOM elements required for a scrollbar. * The resulting widget is not sized. From b22b25e8f8ca7e03288708f143ddb8260720a63b Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Wed, 13 Apr 2016 14:04:14 -0700 Subject: [PATCH 10/32] correctly position the trash can and zoom controls for RTL, toolbox at left --- core/trashcan.js | 4 ++++ core/zoom_controls.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/core/trashcan.js b/core/trashcan.js index 429d613c..a9e670c5 100644 --- a/core/trashcan.js +++ b/core/trashcan.js @@ -220,6 +220,10 @@ Blockly.Trashcan.prototype.position = function() { } if (this.workspace_.RTL) { this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; + if (this.workspace_.toolbox_ && + this.workspace_.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { + this.left_ += metrics.absoluteLeft; + } } else { this.left_ = metrics.viewWidth + metrics.absoluteLeft - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; diff --git a/core/zoom_controls.js b/core/zoom_controls.js index 31a0b212..92279470 100644 --- a/core/zoom_controls.js +++ b/core/zoom_controls.js @@ -208,6 +208,10 @@ Blockly.ZoomControls.prototype.position = function() { } if (this.workspace_.RTL) { this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; + if (this.workspace_.toolbox_ && + this.workspace_.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { + this.left_ += metrics.absoluteLeft; + } } else { this.left_ = metrics.viewWidth + metrics.absoluteLeft - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; From 21e999678ce12d3ed6addf135b921e30a1914e45 Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Wed, 13 Apr 2016 15:30:11 -0700 Subject: [PATCH 11/32] correctly position trash can and zoom controls when the flyout is always open; add flyout and toolbox size to main workspace metrics; get rid of flyout's vertical offset. --- core/blockly.js | 26 ++++++++++++++++++-------- core/flyout.js | 38 +++++++++++++++++++++----------------- core/toolbox.js | 18 ++++++++++++++++-- core/trashcan.js | 16 +++++++++++++--- core/workspace.js | 1 + core/zoom_controls.js | 15 ++++++++++++--- 6 files changed, 81 insertions(+), 33 deletions(-) diff --git a/core/blockly.js b/core/blockly.js index f3f99766..76e6ef83 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -406,6 +406,11 @@ Blockly.hideChaff = function(opt_allowToolbox) { * .contentLeft: Offset of the left-most content from the x=0 coordinate. * .absoluteTop: Top-edge of view. * .absoluteLeft: Left-edge of view. + * .toolboxWidth: Width of toolbox, if it exists. Otherwise zero. + * .toolboxHeight: Height of toolbox, if it exists. Otherwise zero. + * .flyoutWidth: Width of the flyout if it is always open. Otherwise zero. + * .flyoutHeight: Height of flyout if it is always open. Otherwise zero. + * .toolboxPosition: Top, bottom, left or right. * @return {Object} Contains size and position metrics of main workspace. * @private * @this Blockly.WorkspaceSvg @@ -415,10 +420,10 @@ Blockly.getMainWorkspaceMetrics_ = function() { if (this.toolbox_) { // If the toolbox is at the bottom it's laid out separately from the main // workspace, rather than overlapping. - if (this.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { - svgSize.height -= this.toolbox_.height; + if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { + svgSize.height -= this.toolbox_.getHeight(); } else { - svgSize.width -= this.toolbox_.width; + svgSize.width -= this.toolbox_.getWidth(); } } // Set the margin to match the flyout's margin so that the workspace does @@ -451,12 +456,12 @@ Blockly.getMainWorkspaceMetrics_ = function() { var bottomEdge = topEdge + blockBox.height; } var absoluteLeft = 0; - if (this.toolbox_ && this.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - absoluteLeft = this.toolbox_.width; + if (this.toolbox_ && this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { + absoluteLeft = this.toolbox_.getWidth(); } var absoluteTop = 0; - if (this.toolbox_ && this.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { - absoluteTop = this.toolbox_.height; + if (this.toolbox_ && this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { + absoluteTop = this.toolbox_.getHeight(); } var metrics = { @@ -469,7 +474,12 @@ Blockly.getMainWorkspaceMetrics_ = function() { contentTop: topEdge, contentLeft: leftEdge, absoluteTop: absoluteTop, - absoluteLeft: absoluteLeft + absoluteLeft: absoluteLeft, + toolboxWidth: this.toolbox_ ? this.toolbox_.getWidth() : 0, + toolboxHeight: this.toolbox_ ? this.toolbox_.getHeight() : 0, + flyoutWidth: this.flyout_ ? this.flyout_.getWidth() : 0, + flyoutHeight: this.flyout_ ? this.flyout_.getHeight() : 0, + toolboxPosition: this.toolboxPosition }; return metrics; }; diff --git a/core/flyout.js b/core/flyout.js index bd92ae83..c5999881 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -134,13 +134,6 @@ Blockly.Flyout.prototype.width_ = 0; */ Blockly.Flyout.prototype.height_ = 0; -/** - * Vertical offset of flyout. - * @type {number} - * @private - */ -Blockly.Flyout.prototype.verticalOffset_ = 0; - /** * Creates the flyout's DOM. Only needs to be called once. * @return {!Element} The flyout's SVG group. @@ -213,6 +206,22 @@ Blockly.Flyout.prototype.dispose = function() { this.targetWorkspace_ = null; }; +/** + * Get the width of the flyout. + * @return {number} the width of the flyout. + */ +Blockly.Flyout.prototype.getWidth = function() { + return this.width_; +}; + +/** + * Get the height of the flyout. + * @return {number} the width of the flyout. + */ +Blockly.Flyout.prototype.getHeight = function() { + return this.height_; +}; + /** * Return an object with all the metrics required to size scrollbars for the * flyout. The following properties are computed: @@ -242,7 +251,7 @@ Blockly.Flyout.prototype.getMetrics_ = function() { var optionBox = {height: 0, y: 0}; } - var absoluteTop = this.verticalOffset_ + this.SCROLLBAR_PADDING; + var absoluteTop = this.SCROLLBAR_PADDING; if (this.horizontalLayout_) { if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { absoluteTop = 0; @@ -303,10 +312,6 @@ Blockly.Flyout.prototype.setMetrics_ = function(xyRatio) { this.workspace_.scrollY + metrics.absoluteTop); }; -Blockly.Flyout.prototype.setVerticalOffset = function(verticalOffset) { - this.verticalOffset_ = verticalOffset; -}; - /** * Move the toolbox to the edge of the workspace. */ @@ -326,8 +331,7 @@ Blockly.Flyout.prototype.position = function() { } this.setBackgroundPath_(edgeWidth, - this.horizontalLayout_ ? - this.height_ + this.verticalOffset_ : metrics.viewHeight); + this.horizontalLayout_ ? this.height_ : metrics.viewHeight); var x = metrics.absoluteLeft; if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { @@ -875,8 +879,8 @@ Blockly.Flyout.placeNewBlock_ = function(originBlock, workspace, xyNew.y += workspace.scrollY / workspace.scale - workspace.scrollY; // If the flyout is collapsible and the workspace can't be scrolled. if (workspace.toolbox_ && !workspace.scrollbar) { - xyNew.x += workspace.toolbox_.width / workspace.scale; - xyNew.y += workspace.toolbox_.height / workspace.scale; + xyNew.x += workspace.toolbox_.getWidth() / workspace.scale; + xyNew.y += workspace.toolbox_.getHeight() / workspace.scale; } // Move the new block to where the old block is. @@ -919,7 +923,7 @@ Blockly.Flyout.prototype.getClientRect = function() { return new goog.math.Rect(-BIG_NUM, y - BIG_NUM, BIG_NUM * 2, BIG_NUM + height); } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { - return new goog.math.Rect(-BIG_NUM, y + this.verticalOffset_, BIG_NUM * 2, + return new goog.math.Rect(-BIG_NUM, y, BIG_NUM * 2, BIG_NUM + height); } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { return new goog.math.Rect(x - BIG_NUM, -BIG_NUM, BIG_NUM + width, diff --git a/core/toolbox.js b/core/toolbox.js index df29e5c6..b2c28de1 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -202,6 +202,22 @@ Blockly.Toolbox.prototype.dispose = function() { this.lastCategory_ = null; }; +/** + * Get the width of the toolbox. + * @return {number} the width of the toolbox. + */ +Blockly.Toolbox.prototype.getWidth = function() { + return this.width; +}; + +/** + * Get the height of the toolbox. + * @return {number} the width of the toolbox. + */ +Blockly.Toolbox.prototype.getHeight = function() { + return this.height; +}; + /** * Move the toolbox to the edge. */ @@ -221,11 +237,9 @@ Blockly.Toolbox.prototype.position = function() { this.height = treeDiv.offsetHeight; if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { // Top treeDiv.style.top = svgPosition.y + 'px'; - this.flyout_.setVerticalOffset(treeDiv.offsetHeight); } else { // Bottom var topOfToolbox = svgPosition.y + svgSize.height; treeDiv.style.top = topOfToolbox + 'px'; - this.flyout_.setVerticalOffset(topOfToolbox); } } else { if (this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { // Right diff --git a/core/trashcan.js b/core/trashcan.js index a9e670c5..8d295f89 100644 --- a/core/trashcan.js +++ b/core/trashcan.js @@ -220,16 +220,26 @@ Blockly.Trashcan.prototype.position = function() { } if (this.workspace_.RTL) { this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; - if (this.workspace_.toolbox_ && - this.workspace_.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - this.left_ += metrics.absoluteLeft; + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { + this.left_ += metrics.flyoutWidth; + if (this.workspace_.toolbox_) { + this.left_ += metrics.absoluteLeft; + } } } else { this.left_ = metrics.viewWidth + metrics.absoluteLeft - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; + + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { + this.left_ -= metrics.flyoutWidth; + } } this.top_ = metrics.viewHeight + metrics.absoluteTop - (this.BODY_HEIGHT_ + this.LID_HEIGHT_) - this.bottom_; + + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { + this.top_ -= metrics.flyoutHeight; + } this.svgGroup_.setAttribute('transform', 'translate(' + this.left_ + ',' + this.top_ + ')'); }; diff --git a/core/workspace.js b/core/workspace.js index 725e65f3..9919c0be 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -45,6 +45,7 @@ Blockly.Workspace = function(opt_options) { this.RTL = !!this.options.RTL; /** @type {boolean} */ this.horizontalLayout = !!this.options.horizontalLayout; + this.toolboxPosition = this.options.toolboxPosition; /** @type {!Array.} */ this.topBlocks_ = []; /** @type {!Array.} */ diff --git a/core/zoom_controls.js b/core/zoom_controls.js index 92279470..51aa77b4 100644 --- a/core/zoom_controls.js +++ b/core/zoom_controls.js @@ -208,16 +208,25 @@ Blockly.ZoomControls.prototype.position = function() { } if (this.workspace_.RTL) { this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; - if (this.workspace_.toolbox_ && - this.workspace_.toolbox_.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - this.left_ += metrics.absoluteLeft; + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { + this.left_ += metrics.flyoutWidth; + if (this.workspace_.toolbox_) { + this.left_ += metrics.absoluteLeft; + } } } else { this.left_ = metrics.viewWidth + metrics.absoluteLeft - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; + + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { + this.left_ -= metrics.flyoutWidth; + } } this.top_ = metrics.viewHeight + metrics.absoluteTop - this.HEIGHT_ - this.bottom_; + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { + this.top_ -= metrics.flyoutHeight; + } this.svgGroup_.setAttribute('transform', 'translate(' + this.left_ + ',' + this.top_ + ')'); }; From a1509a4467f2bbab28903cf509c190270503fedd Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Thu, 14 Apr 2016 12:50:10 -0700 Subject: [PATCH 12/32] toolbox -> flyout --- core/flyout.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index c5999881..ab03f46c 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -313,7 +313,7 @@ Blockly.Flyout.prototype.setMetrics_ = function(xyRatio) { }; /** - * Move the toolbox to the edge of the workspace. + * Move the flyout to the edge of the workspace. */ Blockly.Flyout.prototype.position = function() { if (!this.isVisible()) { @@ -367,10 +367,10 @@ Blockly.Flyout.prototype.position = function() { }; /** - * Create and set the path for the visible boundaries of the toolbox. - * @param {number} width The width of the toolbox, not including the + * Create and set the path for the visible boundaries of the flyout. + * @param {number} width The width of the flyout, not including the * rounded corners. - * @param {number} height The height of the toolbox, not including + * @param {number} height The height of the flyout, not including * rounded corners. * @private */ @@ -383,11 +383,11 @@ Blockly.Flyout.prototype.setBackgroundPath_ = function(width, height) { }; /** - * Create and set the path for the visible boundaries of the toolbox in vertical + * Create and set the path for the visible boundaries of the flyout in vertical * mode. - * @param {number} width The width of the toolbox, not including the + * @param {number} width The width of the flyout, not including the * rounded corners. - * @param {number} height The height of the toolbox, not including + * @param {number} height The height of the flyout, not including * rounded corners. * @private */ @@ -416,11 +416,11 @@ Blockly.Flyout.prototype.setBackgroundPathVertical_ = function(width, height) { }; /** - * Create and set the path for the visible boundaries of the toolbox in + * Create and set the path for the visible boundaries of the flyout in * horizontal mode. - * @param {number} width The width of the toolbox, not including the + * @param {number} width The width of the flyout, not including the * rounded corners. - * @param {number} height The height of the toolbox, not including + * @param {number} height The height of the flyout, not including * rounded corners. * @private */ From 4fd9f096deb4b81955a9c0610234bb320c56d548 Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Thu, 14 Apr 2016 13:21:35 -0700 Subject: [PATCH 13/32] decompose Blockly.Flyout.prototype.show --- core/flyout.js | 132 ++++++++++++++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 57 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index ab03f46c..d6fed145 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -531,18 +531,7 @@ Blockly.Flyout.prototype.hide = function() { */ Blockly.Flyout.prototype.show = function(xmlList) { this.hide(); - // Delete any blocks from a previous showing. - var blocks = this.workspace_.getTopBlocks(false); - for (var i = 0, block; block = blocks[i]; i++) { - if (block.workspace == this.workspace_) { - block.dispose(false, false); - } - } - // Delete any background buttons from a previous showing. - for (var i = 0, rect; rect = this.buttons_[i]; i++) { - goog.dom.removeNode(rect); - } - this.buttons_.length = 0; + this.clearOldBlocks_(); if (xmlList == Blockly.Variables.NAME_TYPE) { // Special category for variables. @@ -562,25 +551,60 @@ Blockly.Flyout.prototype.show = function(xmlList) { this.permanentlyDisabled_.length = 0; for (var i = 0, xml; xml = xmlList[i]; i++) { if (xml.tagName && xml.tagName.toUpperCase() == 'BLOCK') { - var block = Blockly.Xml.domToBlock(xml, this.workspace_); - if (block.disabled) { + var curBlock = Blockly.Xml.domToBlock(xml, this.workspace_); + if (curBlock.disabled) { // Record blocks that were initially disabled. // Do not enable these blocks as a result of capacity filtering. - this.permanentlyDisabled_.push(block); + this.permanentlyDisabled_.push(curBlock); } - blocks.push(block); + blocks.push(curBlock); var gap = parseInt(xml.getAttribute('gap'), 10); gaps.push(isNaN(gap) ? margin * 3 : gap); } } + this.layoutBlocks_(blocks, gaps, margin); + + // IE 11 is an incompetant browser that fails to fire mouseout events. + // When the mouse is over the background, deselect all blocks. + var deselectAll = function(e) { + var blocks = this.workspace_.getTopBlocks(false); + for (var i = 0, block; block = blocks[i]; i++) { + block.removeSelect(); + } + }; + this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover', + this, deselectAll)); + + if (this.horizontalLayout_) { + this.height_ = 0; + } else { + this.width_ = 0; + } + this.reflow(); + + this.filterForCapacity_(); + + // Fire a resize event to update the flyout's scrollbar. + Blockly.fireUiEventNow(window, 'resize'); + this.reflowWrapper_ = this.reflow.bind(this); + this.workspace_.addChangeListener(this.reflowWrapper_); +}; + +/** + * Lay out the blocks in the flyout. + * @param {!Array} blocks The blocks to lay out. + * @param {!Array} gaps The visible gaps between blocks. + * @param {number} margin The margin around the edges of the flyout. + * @private + */ +Blockly.Flyout.prototype.layoutBlocks_ = function(blocks, gaps, margin) { // The blocks need to be visible in order to be laid out and measured // correctly, but we don't want the flyout to show up until it's properly // sized. Opacity is reset at the end of position(). this.svgGroup_.style.opacity = 0; this.svgGroup_.style.display = 'block'; - // Lay out the blocks. var cursorX = margin / this.workspace_.scale + Blockly.BlockSvg.TAB_WIDTH; var cursorY = margin; for (var i = 0, block; block = blocks[i]; i++) { @@ -612,31 +636,25 @@ Blockly.Flyout.prototype.show = function(xmlList) { this.addBlockListeners_(root, block, rect); } +}; - // IE 11 is an incompetant browser that fails to fire mouseout events. - // When the mouse is over the background, deselect all blocks. - var deselectAll = function(e) { - var blocks = this.workspace_.getTopBlocks(false); - for (var i = 0, block; block = blocks[i]; i++) { - block.removeSelect(); +/** + * Delete blocks and background buttons from a previous showing of the flyout. + * @private + */ +Blockly.Flyout.prototype.clearOldBlocks_ = function() { + // Delete any blocks from a previous showing. + var oldBlocks = this.workspace_.getTopBlocks(false); + for (var i = 0, block; block = oldBlocks[i]; i++) { + if (block.workspace == this.workspace_) { + block.dispose(false, false); } - }; - this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover', - this, deselectAll)); - - if (this.horizontalLayout_) { - this.height_ = 0; - } else { - this.width_ = 0; } - this.reflow(); - - this.filterForCapacity_(); - - // Fire a resize event to update the flyout's scrollbar. - Blockly.fireUiEventNow(window, 'resize'); - this.reflowWrapper_ = this.reflow.bind(this); - this.workspace_.addChangeListener(this.reflowWrapper_); + // Delete any background buttons from a previous showing. + for (var j = 0, rect; rect = this.buttons_[j]; j++) { + goog.dom.removeNode(rect); + } + this.buttons_.length = 0; }; /** @@ -648,23 +666,23 @@ Blockly.Flyout.prototype.show = function(xmlList) { * @private */ Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) { - if (this.autoClose) { - this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, - this.createBlockFunc_(block))); - } else { - this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, - this.blockMouseDown_(block))); - } - this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block, - block.addSelect)); - this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block, - block.removeSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, + if (this.autoClose) { + this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, this.createBlockFunc_(block))); - this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block, - block.addSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block, - block.removeSelect)); + } else { + this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, + this.blockMouseDown_(block))); + } + this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block, + block.addSelect)); + this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block, + block.removeSelect)); + this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, + this.createBlockFunc_(block))); + this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block, + block.addSelect)); + this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block, + block.removeSelect)); }; /** @@ -974,7 +992,7 @@ Blockly.Flyout.prototype.reflowHorizontal = function() { flyoutHeight *= this.workspace_.scale; flyoutHeight += margin * 1.5 + Blockly.Scrollbar.scrollbarThickness; if (this.height_ != flyoutHeight) { - for (var i = 0, block; block = blocks[i]; i++) { + for (i = 0, block; block = blocks[i]; i++) { var blockHW = block.getHeightWidth(); if (block.flyoutRect_) { block.flyoutRect_.setAttribute('width', blockHW.width); @@ -987,7 +1005,7 @@ Blockly.Flyout.prototype.reflowHorizontal = function() { this.RTL ? blockXY.x - blockHW.width + tab : blockXY.x - tab); } } - // Record the width for .getMetrics_ and .position. + // Record the height for .getMetrics_ and .position. this.height_ = flyoutHeight; } }; From 84d2388add4d3daad31a6621f9e9dd78e6f1f6d1 Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Mon, 18 Apr 2016 17:29:48 -0700 Subject: [PATCH 14/32] works in horizontal rtl! --- core/flyout.js | 87 +++++++++++++++++++++++++++++++++++------------ core/scrollbar.js | 3 +- 2 files changed, 68 insertions(+), 22 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index d6fed145..bca5f284 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -113,6 +113,13 @@ Blockly.Flyout.prototype.autoClose = true; */ Blockly.Flyout.prototype.CORNER_RADIUS = 8; +/** + * Margin around the edges of the blocks in the flyout. + * @type {number} + * @const + */ +Blockly.Flyout.prototype.MARGIN = Blockly.Flyout.prototype.CORNER_RADIUS; + /** * Top/bottom padding between scrollbar and edge of flyout background. * @type {number} @@ -248,7 +255,7 @@ Blockly.Flyout.prototype.getMetrics_ = function() { var optionBox = this.workspace_.getCanvas().getBBox(); } catch (e) { // Firefox has trouble with hidden elements (Bug 528969). - var optionBox = {height: 0, y: 0}; + var optionBox = {height: 0, y: 0, width: 0, x: 0}; } var absoluteTop = this.SCROLLBAR_PADDING; @@ -258,7 +265,7 @@ Blockly.Flyout.prototype.getMetrics_ = function() { } var viewHeight = this.height_; if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) { - viewHeight += this.CORNER_RADIUS - this.SCROLLBAR_PADDING; + viewHeight += this.MARGIN - this.SCROLLBAR_PADDING; } var viewWidth = this.width_ - 2 * this.SCROLLBAR_PADDING; } else { @@ -273,13 +280,13 @@ Blockly.Flyout.prototype.getMetrics_ = function() { viewHeight: viewHeight, viewWidth: viewWidth, contentHeight: (optionBox.height) * this.workspace_.scale, - contentWidth: (optionBox.width) * this.workspace_.scale, + contentWidth: (optionBox.width + 2 * this.MARGIN) * this.workspace_.scale, viewTop: -this.workspace_.scrollY, viewLeft: -this.workspace_.scrollX, contentTop: optionBox.y, contentLeft: 0, absoluteTop: absoluteTop, - absoluteLeft: this.SCROLLBAR_PADDING + absoluteLeft: this.horizontalLayout ? 0 : this.SCROLLBAR_PADDING }; return metrics; }; @@ -302,13 +309,10 @@ Blockly.Flyout.prototype.setMetrics_ = function(xyRatio) { -metrics.contentHeight * xyRatio.y - metrics.contentTop; } else if (this.horizontalLayout_ && goog.isNumber(xyRatio.x)) { this.workspace_.scrollX = - -metrics.contentWidth * xyRatio.x + - (this.RTL ? metrics.contentLeft : -metrics.contentLeft); + -metrics.contentWidth * xyRatio.x - metrics.contentLeft; } - var translateX = this.horizontalLayout_ && this.RTL ? - metrics.absoluteLeft + metrics.viewWidth - this.workspace_.scrollX : - this.workspace_.scrollX + metrics.absoluteLeft; - this.workspace_.translate(translateX, + + this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft, this.workspace_.scrollY + metrics.absoluteTop); }; @@ -464,7 +468,7 @@ Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = * Scroll the flyout to the top. */ Blockly.Flyout.prototype.scrollToStart = function() { - this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? 1000000000 : 0); + this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? 100000 : 0); }; /** @@ -581,8 +585,9 @@ Blockly.Flyout.prototype.show = function(xmlList) { } else { this.width_ = 0; } - this.reflow(); + this.reflow(true /* opt_skip_resize */); + this.offsetHorizontalRtlBlocks(this.workspace_.getTopBlocks(false)); this.filterForCapacity_(); // Fire a resize event to update the flyout's scrollbar. @@ -753,6 +758,7 @@ Blockly.Flyout.prototype.onMouseMove_ = function(e) { this.startDragMouseX_ = e.clientX; var x = metrics.viewLeft - dx; x = goog.math.clamp(x, 0, metrics.contentWidth - metrics.viewWidth); + this.scrollbar_.set(metrics.contentLeft - x); this.scrollbar_.set(x); } else { if (metrics.contentHeight - metrics.viewHeight < 0) { @@ -980,12 +986,12 @@ Blockly.Flyout.terminateDrag_ = function() { /** * Compute height of flyout. Position button under each block. * For RTL: Lay out the blocks right-aligned. + * @param {!Array} blocks The blocks to reflow. */ -Blockly.Flyout.prototype.reflowHorizontal = function() { +Blockly.Flyout.prototype.reflowHorizontal = function(blocks) { this.workspace_.scale = this.targetWorkspace_.scale; var flyoutHeight = 0; var margin = this.CORNER_RADIUS; - var blocks = this.workspace_.getTopBlocks(false); for (var i = 0, block; block = blocks[i]; i++) { flyoutHeight = Math.max(flyoutHeight, block.getHeightWidth().height); } @@ -997,7 +1003,7 @@ Blockly.Flyout.prototype.reflowHorizontal = function() { if (block.flyoutRect_) { block.flyoutRect_.setAttribute('width', blockHW.width); block.flyoutRect_.setAttribute('height', blockHW.height); - // Blocks with output tabs are shifted a bit. + // Rectangles behind blocks with output tabs are shifted a bit. var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0; var blockXY = block.getRelativeToSurfaceXY(); block.flyoutRect_.setAttribute('y', blockXY.y); @@ -1013,12 +1019,12 @@ Blockly.Flyout.prototype.reflowHorizontal = function() { /** * Compute width of flyout. Position button under each block. * For RTL: Lay out the blocks right-aligned. + * @param {!Array} blocks The blocks to reflow. */ -Blockly.Flyout.prototype.reflowVertical = function() { +Blockly.Flyout.prototype.reflowVertical = function(blocks) { this.workspace_.scale = this.targetWorkspace_.scale; var flyoutWidth = 0; var margin = this.CORNER_RADIUS; - var blocks = this.workspace_.getTopBlocks(false); for (var i = 0, block; block = blocks[i]; i++) { var width = block.getHeightWidth().width; if (block.outputConnection) { @@ -1056,12 +1062,51 @@ Blockly.Flyout.prototype.reflowVertical = function() { } }; -Blockly.Flyout.prototype.reflow = function() { +/** + * Reflow blocks and their buttons. + * @param {boolean} opt_skip_resize Possibly skip resizing, since sometimes a + * resize will be called immediately anyway. + */ +Blockly.Flyout.prototype.reflow = function(opt_skip_resize) { + var blocks = this.workspace_.getTopBlocks(false); if (this.horizontalLayout_) { - this.reflowHorizontal(); + this.reflowHorizontal(blocks); } else { - this.reflowVertical(); + this.reflowVertical(blocks); } // Fire a resize event to update the flyout's scrollbar. - Blockly.fireUiEvent(window, 'resize'); + if (!opt_skip_resize) { + Blockly.fireUiEvent(window, 'resize'); + } +}; + +/** + * In the horizontal RTL case all of the blocks will be laid out to the left of + * the origin, but we won't know how big the workspace is until the layout pass + * is done. + * Now that it's done, shunt all the blocks to be right of the origin. + * @param {!Array} blocks The blocks to repositions. + */ +Blockly.Flyout.prototype.offsetHorizontalRtlBlocks = function(blocks) { + if (this.horizontalLayout_ && this.RTL) { + try { + var optionBox = this.workspace_.getCanvas().getBBox(); + } catch (e) { + // Firefox has trouble with hidden elements (Bug 528969). + optionBox = {height: 0, y: 0, width: 0, x: 0}; + } + + console.log(optionBox); + var offset = Math.max(-optionBox.x, this.width_ / this.workspace_.scale); + console.log(this.width_); + console.log(offset); + + for (var i = 0, block; block = blocks[i]; i++) { + block.moveBy(offset, 0); + if (block.flyoutRect_) { + block.flyoutRect_.setAttribute('x', + offset + Number(block.flyoutRect_.getAttribute('x'))); + } + } + } }; diff --git a/core/scrollbar.js b/core/scrollbar.js index b8534d75..cd33d5fa 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -555,8 +555,9 @@ Blockly.Scrollbar.prototype.onScroll_ = function() { * @param {number} value The distance from the top/left end of the bar. */ Blockly.Scrollbar.prototype.set = function(value) { + var constrainedValue = this.constrainKnob_(value * this.ratio_); // Move the scrollbar slider. - this.svgKnob_.setAttribute(this.horizontal_ ? 'x' : 'y', value * this.ratio_); + this.svgKnob_.setAttribute(this.horizontal_ ? 'x' : 'y', constrainedValue); this.onScroll_(); }; From 8882c727a3bc9532e664b651adfa10dd3c781857 Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Tue, 19 Apr 2016 14:27:13 -0700 Subject: [PATCH 15/32] remove log statements --- core/flyout.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index bca5f284..267a01ef 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -1096,10 +1096,7 @@ Blockly.Flyout.prototype.offsetHorizontalRtlBlocks = function(blocks) { optionBox = {height: 0, y: 0, width: 0, x: 0}; } - console.log(optionBox); var offset = Math.max(-optionBox.x, this.width_ / this.workspace_.scale); - console.log(this.width_); - console.log(offset); for (var i = 0, block; block = blocks[i]; i++) { block.moveBy(offset, 0); From ac15b8b17924a70f52de50c5edadb2ad40b17fff Mon Sep 17 00:00:00 2001 From: rachel-fenichel Date: Tue, 19 Apr 2016 14:37:50 -0700 Subject: [PATCH 16/32] Use Infinity instead of a large number --- core/flyout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/flyout.js b/core/flyout.js index 267a01ef..29453a5e 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -468,7 +468,7 @@ Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = * Scroll the flyout to the top. */ Blockly.Flyout.prototype.scrollToStart = function() { - this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? 100000 : 0); + this.scrollbar_.set((this.horizontalLayout_ && this.RTL) ? Infinity : 0); }; /** From de58c07286fd7589e75ac3b007f4e91e0f63a86b Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 25 Apr 2016 14:31:47 -0700 Subject: [PATCH 17/32] Shift scrollbar position slightly --- core/scrollbar.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/scrollbar.js b/core/scrollbar.js index 6a3e738b..14ee7eac 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -306,8 +306,7 @@ Blockly.Scrollbar.prototype.resizeHorizontal_ = function(hostMetrics) { this.svgKnob_.setAttribute('width', Math.max(0, innerLength)); this.xCoordinate = hostMetrics.absoluteLeft + 0.5; if (this.pair_ && this.workspace_.RTL) { - //this.xCoordinate += hostMetrics.absoluteLeft + - // Blockly.Scrollbar.scrollbarThickness; + this.xCoordinate += Blockly.Scrollbar.scrollbarThickness; } // Horizontal toolbar should always be just above the bottom of the workspace. this.yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight - From 60dfbb455ed29e3588f2d4cedf321c8276c97568 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 26 Apr 2016 15:10:49 -0700 Subject: [PATCH 18/32] offset correctly when opening for the first time --- core/flyout.js | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index 456429df..fba2b0db 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -260,6 +260,7 @@ Blockly.Flyout.prototype.getMetrics_ = function() { } var absoluteTop = this.SCROLLBAR_PADDING; + var absoluteLeft = this.SCROLLBAR_PADDING; if (this.horizontalLayout_) { if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { absoluteTop = 0; @@ -270,6 +271,7 @@ Blockly.Flyout.prototype.getMetrics_ = function() { } var viewWidth = this.width_ - 2 * this.SCROLLBAR_PADDING; } else { + absoluteLeft = 0; var viewHeight = this.height_ - 2 * this.SCROLLBAR_PADDING; var viewWidth = this.width_; if (!this.RTL) { @@ -287,7 +289,7 @@ Blockly.Flyout.prototype.getMetrics_ = function() { contentTop: optionBox.y, contentLeft: 0, absoluteTop: absoluteTop, - absoluteLeft: this.horizontalLayout ? 0 : this.SCROLLBAR_PADDING + absoluteLeft: absoluteLeft }; return metrics; }; @@ -586,7 +588,7 @@ Blockly.Flyout.prototype.show = function(xmlList) { } else { this.width_ = 0; } - this.reflow(true /* opt_skip_resize */); + this.reflow(); this.offsetHorizontalRtlBlocks(this.workspace_.getTopBlocks(false)); this.filterForCapacity_(); @@ -624,10 +626,14 @@ Blockly.Flyout.prototype.layoutBlocks_ = function(blocks, gaps, margin) { block.render(); var root = block.getSvgRoot(); var blockHW = block.getHeightWidth(); + var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0; + if (this.horizontalLayout_) { + cursorX += tab; + } block.moveBy((this.horizontalLayout_ && this.RTL) ? -cursorX : cursorX, cursorY); if (this.horizontalLayout_) { - cursorX += blockHW.width + gaps[i]; + cursorX += (blockHW.width + gaps[i] - tab); } else { cursorY += blockHW.height + gaps[i]; } @@ -726,16 +732,18 @@ Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) { if (this.autoClose) { this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, this.createBlockFunc_(block))); + this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, + this.createBlockFunc_(block))); } else { this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, this.blockMouseDown_(block))); + this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, + this.blockMouseDown_(block))); } this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block, block.addSelect)); this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block, block.removeSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, - this.createBlockFunc_(block))); this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block, block.addSelect)); this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block, @@ -1054,6 +1062,8 @@ Blockly.Flyout.terminateDrag_ = function() { * Compute height of flyout. Position button under each block. * For RTL: Lay out the blocks right-aligned. * @param {!Array} blocks The blocks to reflow. + * @param {boolean} opt_skip_resize Possibly skip resizing, since sometimes a + * resize will be called immediately anyway. */ Blockly.Flyout.prototype.reflowHorizontal = function(blocks) { this.workspace_.scale = this.targetWorkspace_.scale; @@ -1088,6 +1098,7 @@ Blockly.Flyout.prototype.reflowHorizontal = function(blocks) { } // Record the height for .getMetrics_ and .position. this.height_ = flyoutHeight; + Blockly.fireUiEvent(window, 'resize'); } }; @@ -1095,6 +1106,8 @@ Blockly.Flyout.prototype.reflowHorizontal = function(blocks) { * Compute width of flyout. Position button under each block. * For RTL: Lay out the blocks right-aligned. * @param {!Array} blocks The blocks to reflow. + * @param {boolean} opt_skip_resize Possibly skip resizing, since sometimes a + * resize will be called immediately anyway. */ Blockly.Flyout.prototype.reflowVertical = function(blocks) { this.workspace_.scale = this.targetWorkspace_.scale; @@ -1141,25 +1154,22 @@ Blockly.Flyout.prototype.reflowVertical = function(blocks) { } // Record the width for .getMetrics_ and .position. this.width_ = flyoutWidth; + Blockly.fireUiEvent(window, 'resize'); } }; /** * Reflow blocks and their buttons. * @param {boolean} opt_skip_resize Possibly skip resizing, since sometimes a - * resize will be called immediately anyway. + * resize will be called immediately anyway. */ -Blockly.Flyout.prototype.reflow = function(opt_skip_resize) { +Blockly.Flyout.prototype.reflow = function() { var blocks = this.workspace_.getTopBlocks(false); if (this.horizontalLayout_) { this.reflowHorizontal(blocks); } else { this.reflowVertical(blocks); } - // Fire a resize event to update the flyout's scrollbar. - if (!opt_skip_resize) { - Blockly.fireUiEvent(window, 'resize'); - } }; /** @@ -1171,6 +1181,8 @@ Blockly.Flyout.prototype.reflow = function(opt_skip_resize) { */ Blockly.Flyout.prototype.offsetHorizontalRtlBlocks = function(blocks) { if (this.horizontalLayout_ && this.RTL) { + // We don't know this workspace's view width yet. + this.position(); try { var optionBox = this.workspace_.getCanvas().getBBox(); } catch (e) { From 2591e12a46aee33c9418fd3203b0dc8b87218eb0 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 26 Apr 2016 18:16:26 -0700 Subject: [PATCH 19/32] Fix janky scrolling in vertical case --- core/blockly.js | 6 ++++-- core/flyout.js | 39 +++------------------------------------ 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/core/blockly.js b/core/blockly.js index d41dcb44..b5dd821c 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -416,10 +416,12 @@ Blockly.getMainWorkspaceMetrics_ = function() { var svgSize = Blockly.svgSize(this.getParentSvg()); if (this.toolbox_) { // If the toolbox is at the bottom it's laid out separately from the main - // workspace, rather than overlapping. + // workspace, rather than overlapping, so we don't need to take its size + // into account. if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { svgSize.height -= this.toolbox_.getHeight(); - } else { + } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT || + this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { svgSize.width -= this.toolbox_.getWidth(); } } diff --git a/core/flyout.js b/core/flyout.js index fba2b0db..d69563ce 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -287,7 +287,7 @@ Blockly.Flyout.prototype.getMetrics_ = function() { viewTop: -this.workspace_.scrollY, viewLeft: -this.workspace_.scrollX, contentTop: optionBox.y, - contentLeft: 0, + contentLeft: optionBox.x, absoluteTop: absoluteTop, absoluteLeft: absoluteLeft }; @@ -308,11 +308,9 @@ Blockly.Flyout.prototype.setMetrics_ = function(xyRatio) { return; } if (!this.horizontalLayout_ && goog.isNumber(xyRatio.y)) { - this.workspace_.scrollY = - -metrics.contentHeight * xyRatio.y - metrics.contentTop; + this.workspace_.scrollY = -metrics.contentHeight * xyRatio.y; } else if (this.horizontalLayout_ && goog.isNumber(xyRatio.x)) { - this.workspace_.scrollX = - -metrics.contentWidth * xyRatio.x - metrics.contentLeft; + this.workspace_.scrollX = -metrics.contentWidth * xyRatio.x; } this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft, @@ -671,36 +669,6 @@ Blockly.Flyout.prototype.clearOldBlocks_ = function() { this.buttons_.length = 0; }; -/** - * Add listeners to a block that has been added to the flyout. - * @param {!Element} root The root node of the SVG group the block is in. - * @param {!Blockly.Block} block The block to add listeners for. - * @param {!Element} rect The invisible rectangle under the block that acts as - * a button for that block. - * @private - */ -Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) { - if (this.autoClose) { - this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, - this.createBlockFunc_(block))); - this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, - this.createBlockFunc_(block))); - } else { - this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null, - this.blockMouseDown_(block))); - this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null, - this.blockMouseDown_(block))); - } - this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block, - block.addSelect)); - this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block, - block.removeSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block, - block.addSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block, - block.removeSelect)); -}; - /** * Delete blocks and background buttons from a previous showing of the flyout. * @private @@ -835,7 +803,6 @@ Blockly.Flyout.prototype.onMouseMove_ = function(e) { this.startDragMouseX_ = e.clientX; var x = metrics.viewLeft - dx; x = goog.math.clamp(x, 0, metrics.contentWidth - metrics.viewWidth); - this.scrollbar_.set(metrics.contentLeft - x); this.scrollbar_.set(x); } else { if (metrics.contentHeight - metrics.viewHeight < 0) { From cb1188af45c392488c9ae7c08915df9df4e1108e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 27 Apr 2016 10:31:01 -0700 Subject: [PATCH 20/32] Add TODOs --- core/flyout.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index d69563ce..648e46fa 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -282,12 +282,12 @@ Blockly.Flyout.prototype.getMetrics_ = function() { var metrics = { viewHeight: viewHeight, viewWidth: viewWidth, - contentHeight: (optionBox.height) * this.workspace_.scale, + contentHeight: (optionBox.height + 2 * this.MARGIN) * this.workspace_.scale, contentWidth: (optionBox.width + 2 * this.MARGIN) * this.workspace_.scale, viewTop: -this.workspace_.scrollY, viewLeft: -this.workspace_.scrollX, - contentTop: optionBox.y, - contentLeft: optionBox.x, + contentTop: 0, // TODO: #349 + contentLeft: 0, // TODO: #349 absoluteTop: absoluteTop, absoluteLeft: absoluteLeft }; From e596e230124c70ce2b9d624c6b2ccae8518ef771 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 27 Apr 2016 16:38:22 -0700 Subject: [PATCH 21/32] Position toolbox correctly at bottom --- core/blockly.js | 6 ++---- core/toolbox.js | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/core/blockly.js b/core/blockly.js index b5dd821c..dc51f2b8 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -415,10 +415,8 @@ Blockly.hideChaff = function(opt_allowToolbox) { Blockly.getMainWorkspaceMetrics_ = function() { var svgSize = Blockly.svgSize(this.getParentSvg()); if (this.toolbox_) { - // If the toolbox is at the bottom it's laid out separately from the main - // workspace, rather than overlapping, so we don't need to take its size - // into account. - if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { + if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP || + this.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { svgSize.height -= this.toolbox_.getHeight(); } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT || this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { diff --git a/core/toolbox.js b/core/toolbox.js index b2c28de1..f49b4c2c 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -238,7 +238,7 @@ Blockly.Toolbox.prototype.position = function() { if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { // Top treeDiv.style.top = svgPosition.y + 'px'; } else { // Bottom - var topOfToolbox = svgPosition.y + svgSize.height; + var topOfToolbox = svgPosition.y + svgSize.height - treeDiv.offsetHeight; treeDiv.style.top = topOfToolbox + 'px'; } } else { From 8f51fb1e48c36592c5890c6deb4a12757d4f35f8 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 2 May 2016 15:23:02 -0700 Subject: [PATCH 22/32] remove duplicate code --- core/flyout.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index 648e46fa..82e1b76b 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -650,25 +650,6 @@ Blockly.Flyout.prototype.layoutBlocks_ = function(blocks, gaps, margin) { } }; -/** - * Delete blocks and background buttons from a previous showing of the flyout. - * @private - */ -Blockly.Flyout.prototype.clearOldBlocks_ = function() { - // Delete any blocks from a previous showing. - var blocks = this.workspace_.getTopBlocks(false); - for (var i = 0, block; block = blocks[i]; i++) { - if (block.workspace == this.workspace_) { - block.dispose(false, false); - } - } - // Delete any background buttons from a previous showing. - for (var j = 0, rect; rect = this.buttons_[j]; j++) { - goog.dom.removeNode(rect); - } - this.buttons_.length = 0; -}; - /** * Delete blocks and background buttons from a previous showing of the flyout. * @private From e0cda58701ce2996883a6c8b8425eef62c3e7011 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 11 May 2016 15:15:12 -0700 Subject: [PATCH 23/32] fix variable naming in createnewblock. --- core/flyout.js | 45 ++++++++++++++++++++------------------------- core/options.js | 5 ----- demos/code/code.js | 2 +- 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index e8a04c54..b99c133a 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -833,7 +833,6 @@ Blockly.Flyout.prototype.onMouseMoveBlock_ = function(e) { */ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { var flyout = this; - var workspace = this.targetWorkspace_; return function(e) { if (Blockly.isRightButton(e)) { // Right-click. Don't create a block, let the context menu show. @@ -844,7 +843,7 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { return; } Blockly.Events.disable(); - var block = flyout.placeNewBlock_(originBlock, workspace); + var block = flyout.placeNewBlock_(originBlock); Blockly.Events.enable(); if (Blockly.Events.isEnabled()) { Blockly.Events.setGroup(true); @@ -864,19 +863,19 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { /** * Copy a block from the flyout to the workspace and position it correctly. - * @param {!Blockly.Block} originBlock The flyout block to copy. - * @param {!Blockly.Workspace} workspace The main workspace. + * @param {!Blockly.Block} originBlock The flyout block to copy.. * @return {!Blockly.Block} The new block in the main workspace. * @private */ -Blockly.Flyout.prototype.placeNewBlock_ = function(originBlock, workspace) { +Blockly.Flyout.prototype.placeNewBlock_ = function(originBlock) { + var targetWorkspace = this.targetWorkspace_; var svgRootOld = originBlock.getSvgRoot(); if (!svgRootOld) { throw 'originBlock is not rendered.'; } // Figure out where the original block is on the screen, relative to the upper // left corner of the main workspace. - var xyOld = Blockly.getSvgXY_(svgRootOld, workspace); + var xyOld = Blockly.getSvgXY_(svgRootOld, targetWorkspace); // Take into account that the flyout might have been scrolled horizontally // (separately from the main workspace). // Generally a no-op in vertical mode but likely to happen in horizontal @@ -888,8 +887,8 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(originBlock, workspace) { // the right of (0, 0) in the main workspace. Offset to take that into // account. if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { - scrollX = workspace.getMetrics().viewWidth - this.width_; - scale = workspace.scale; + scrollX = targetWorkspace.getMetrics().viewWidth - this.width_; + scale = targetWorkspace.scale; // Scale the scroll (getSvgXY_ did not do this). xyOld.x += scrollX / scale - scrollX; } @@ -904,14 +903,14 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(originBlock, workspace) { // If the flyout is on the bottom, (0, 0) in the flyout is offset to be below // (0, 0) in the main workspace. Offset to take that into account. if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { - scrollY = workspace.getMetrics().viewHeight - this.height_; - scale = workspace.scale; + scrollY = targetWorkspace.getMetrics().viewHeight - this.height_; + scale = targetWorkspace.scale; xyOld.y += scrollY / scale - scrollY; } // Create the new block by cloning the block in the flyout (via XML). var xml = Blockly.Xml.blockToDom(originBlock); - var block = Blockly.Xml.domToBlock(xml, workspace); + var block = Blockly.Xml.domToBlock(xml, targetWorkspace); var svgRootNew = block.getSvgRoot(); if (!svgRootNew) { throw 'block is not rendered.'; @@ -920,14 +919,16 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(originBlock, workspace) { // upper left corner of the workspace. This may not be the same as the // original block because the flyout's origin may not be the same as the // main workspace's origin. - var xyNew = Blockly.getSvgXY_(svgRootNew, workspace); + var xyNew = Blockly.getSvgXY_(svgRootNew, targetWorkspace); // Scale the scroll (getSvgXY_ did not do this). - xyNew.x += workspace.scrollX / workspace.scale - workspace.scrollX; - xyNew.y += workspace.scrollY / workspace.scale - workspace.scrollY; + xyNew.x += + targetWorkspace.scrollX / targetWorkspace.scale - targetWorkspace.scrollX; + xyNew.y += + targetWorkspace.scrollY / targetWorkspace.scale - targetWorkspace.scrollY; // If the flyout is collapsible and the workspace can't be scrolled. - if (workspace.toolbox_ && !workspace.scrollbar) { - xyNew.x += workspace.toolbox_.getWidth() / workspace.scale; - xyNew.y += workspace.toolbox_.getHeight() / workspace.scale; + if (targetWorkspace.toolbox_ && !targetWorkspace.scrollbar) { + xyNew.x += targetWorkspace.toolbox_.getWidth() / targetWorkspace.scale; + xyNew.y += targetWorkspace.toolbox_.getHeight() / targetWorkspace.scale; } // Move the new block to where the old block is. @@ -1010,8 +1011,6 @@ Blockly.Flyout.terminateDrag_ = function() { * Compute height of flyout. Position button under each block. * For RTL: Lay out the blocks right-aligned. * @param {!Array} blocks The blocks to reflow. - * @param {boolean} opt_skip_resize Possibly skip resizing, since sometimes a - * resize will be called immediately anyway. */ Blockly.Flyout.prototype.reflowHorizontal = function(blocks) { this.workspace_.scale = this.targetWorkspace_.scale; @@ -1046,7 +1045,7 @@ Blockly.Flyout.prototype.reflowHorizontal = function(blocks) { } // Record the height for .getMetrics_ and .position. this.height_ = flyoutHeight; - Blockly.fireUiEvent(window, 'resize'); + Blockly.asyncSvgResize(this.workspace_); } }; @@ -1054,8 +1053,6 @@ Blockly.Flyout.prototype.reflowHorizontal = function(blocks) { * Compute width of flyout. Position button under each block. * For RTL: Lay out the blocks right-aligned. * @param {!Array} blocks The blocks to reflow. - * @param {boolean} opt_skip_resize Possibly skip resizing, since sometimes a - * resize will be called immediately anyway. */ Blockly.Flyout.prototype.reflowVertical = function(blocks) { this.workspace_.scale = this.targetWorkspace_.scale; @@ -1102,14 +1099,12 @@ Blockly.Flyout.prototype.reflowVertical = function(blocks) { } // Record the width for .getMetrics_ and .position. this.width_ = flyoutWidth; - Blockly.fireUiEvent(window, 'resize'); + Blockly.asyncSvgResize(this.workspace_); } }; /** * Reflow blocks and their buttons. - * @param {boolean} opt_skip_resize Possibly skip resizing, since sometimes a - * resize will be called immediately anyway. */ Blockly.Flyout.prototype.reflow = function() { var blocks = this.workspace_.getTopBlocks(false); diff --git a/core/options.js b/core/options.js index 96fa57be..d8da08b6 100644 --- a/core/options.js +++ b/core/options.js @@ -105,9 +105,6 @@ Blockly.Options = function(options) { pathToMedia = options['path'] + 'media/'; } - var enableRealtime = !!options['realtime']; - var realtimeOptions = enableRealtime ? options['realtimeOptions'] : undefined; - this.RTL = rtl; this.collapse = hasCollapse; this.comments = hasComments; @@ -124,8 +121,6 @@ Blockly.Options = function(options) { this.languageTree = languageTree; this.gridOptions = Blockly.Options.parseGridOptions_(options); this.zoomOptions = Blockly.Options.parseZoomOptions_(options); - this.enableRealtime = enableRealtime; - this.realtimeOptions = realtimeOptions; this.toolboxPosition = toolboxPosition; }; diff --git a/demos/code/code.js b/demos/code/code.js index c095e5e3..e75b452d 100644 --- a/demos/code/code.js +++ b/demos/code/code.js @@ -294,7 +294,7 @@ Code.tabClick = function(clickedName) { if (clickedName == 'blocks') { Code.workspace.setVisible(true); } - Blockly.fireUiEvent(window, 'resize'); + Blockly.asyncSvgResize(this.workspace_); }; /** From 8cfd48554ae3801592c350c3e47d48b30d821694 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 11 May 2016 16:40:42 -0700 Subject: [PATCH 24/32] clean up margins --- core/flyout.js | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index b99c133a..80a5e0de 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -350,8 +350,7 @@ Blockly.Flyout.prototype.position = function() { y -= this.height_; } - this.svgGroup_.setAttribute('transform', - 'translate(' + x + ',' + y + ')'); + this.svgGroup_.setAttribute('transform', 'translate(' + x + ',' + y + ')'); // Record the height for Blockly.Flyout.getMetrics_, or width if the layout is // horizontal. @@ -365,10 +364,6 @@ Blockly.Flyout.prototype.position = function() { if (this.scrollbar_) { this.scrollbar_.resize(); } - // The blocks need to be visible in order to be laid out and measured - // correctly, but we don't want the flyout to show up until it's properly - // sized. Opacity is set to zero in show(). - this.svgGroup_.style.opacity = 1; }; /** @@ -548,7 +543,6 @@ Blockly.Flyout.prototype.show = function(xmlList) { Blockly.Procedures.flyoutCategory(this.workspace_.targetWorkspace); } - var margin = this.CORNER_RADIUS; this.svgGroup_.style.display = 'block'; // Create the blocks to be shown in this flyout. var blocks = []; @@ -564,11 +558,11 @@ Blockly.Flyout.prototype.show = function(xmlList) { } blocks.push(curBlock); var gap = parseInt(xml.getAttribute('gap'), 10); - gaps.push(isNaN(gap) ? margin * 3 : gap); + gaps.push(isNaN(gap) ? this.MARGIN * 3 : gap); } } - this.layoutBlocks_(blocks, gaps, margin); + this.layoutBlocks_(blocks, gaps); // IE 11 is an incompetant browser that fails to fire mouseout events. // When the mouse is over the background, deselect all blocks. @@ -601,17 +595,11 @@ Blockly.Flyout.prototype.show = function(xmlList) { * Lay out the blocks in the flyout. * @param {!Array.} blocks The blocks to lay out. * @param {!Array.} gaps The visible gaps between blocks. - * @param {number} margin The margin around the edges of the flyout. * @private */ -Blockly.Flyout.prototype.layoutBlocks_ = function(blocks, gaps, margin) { - // The blocks need to be visible in order to be laid out and measured - // correctly, but we don't want the flyout to show up until it's properly - // sized. Opacity is reset at the end of position(). - this.svgGroup_.style.opacity = 0; - this.svgGroup_.style.display = 'block'; - - var cursorX = margin / this.workspace_.scale + Blockly.BlockSvg.TAB_WIDTH; +Blockly.Flyout.prototype.layoutBlocks_ = function(blocks, gaps) { + var margin = this.MARGIN * this.workspace_.scale; + var cursorX = this.RTL ? margin : margin + Blockly.BlockSvg.TAB_WIDTH; var cursorY = margin; for (var i = 0, block; block = blocks[i]; i++) { var allBlocks = block.getDescendants(); @@ -628,8 +616,8 @@ Blockly.Flyout.prototype.layoutBlocks_ = function(blocks, gaps, margin) { if (this.horizontalLayout_) { cursorX += tab; } - block.moveBy((this.horizontalLayout_ && this.RTL) ? - -cursorX : cursorX, cursorY); + block.moveBy((this.horizontalLayout_ && this.RTL) ? -cursorX : cursorX, + cursorY); if (this.horizontalLayout_) { cursorX += (blockHW.width + gaps[i] - tab); } else { @@ -1015,12 +1003,12 @@ Blockly.Flyout.terminateDrag_ = function() { Blockly.Flyout.prototype.reflowHorizontal = function(blocks) { this.workspace_.scale = this.targetWorkspace_.scale; var flyoutHeight = 0; - var margin = this.CORNER_RADIUS; for (var i = 0, block; block = blocks[i]; i++) { flyoutHeight = Math.max(flyoutHeight, block.getHeightWidth().height); } + flyoutHeight += this.MARGIN * 1.5; flyoutHeight *= this.workspace_.scale; - flyoutHeight += margin * 1.5 + Blockly.Scrollbar.scrollbarThickness; + flyoutHeight += Blockly.Scrollbar.scrollbarThickness; if (this.height_ != flyoutHeight) { for (i = 0, block; block = blocks[i]; i++) { var blockHW = block.getHeightWidth(); @@ -1057,7 +1045,6 @@ Blockly.Flyout.prototype.reflowHorizontal = function(blocks) { Blockly.Flyout.prototype.reflowVertical = function(blocks) { this.workspace_.scale = this.targetWorkspace_.scale; var flyoutWidth = 0; - var margin = this.CORNER_RADIUS; for (var i = 0, block; block = blocks[i]; i++) { var width = block.getHeightWidth().width; if (block.outputConnection) { @@ -1065,16 +1052,16 @@ Blockly.Flyout.prototype.reflowVertical = function(blocks) { } flyoutWidth = Math.max(flyoutWidth, width); } - flyoutWidth += Blockly.BlockSvg.TAB_WIDTH; + flyoutWidth += this.MARGIN * 1.5 + Blockly.BlockSvg.TAB_WIDTH; flyoutWidth *= this.workspace_.scale; - flyoutWidth += margin * 1.5 + Blockly.Scrollbar.scrollbarThickness; + flyoutWidth += Blockly.Scrollbar.scrollbarThickness; if (this.width_ != flyoutWidth) { for (var i = 0, block; block = blocks[i]; i++) { var blockHW = block.getHeightWidth(); if (this.RTL) { // With the flyoutWidth known, right-align the blocks. var oldX = block.getRelativeToSurfaceXY().x; - var dx = flyoutWidth - margin; + var dx = flyoutWidth - this.MARGIN; dx /= this.workspace_.scale; dx -= Blockly.BlockSvg.TAB_WIDTH; block.moveBy(dx - oldX, 0); @@ -1120,7 +1107,7 @@ Blockly.Flyout.prototype.reflow = function() { * the origin, but we won't know how big the workspace is until the layout pass * is done. * Now that it's done, shunt all the blocks to be right of the origin. - * @param {!Array} blocks The blocks to repositions. + * @param {!Array} blocks The blocks to reposition. */ Blockly.Flyout.prototype.offsetHorizontalRtlBlocks = function(blocks) { if (this.horizontalLayout_ && this.RTL) { @@ -1133,7 +1120,8 @@ Blockly.Flyout.prototype.offsetHorizontalRtlBlocks = function(blocks) { optionBox = {height: 0, y: 0, width: 0, x: 0}; } - var offset = Math.max(-optionBox.x, this.width_ / this.workspace_.scale); + var offset = Math.max(-optionBox.x + this.MARGIN, + this.width_ / this.workspace_.scale); for (var i = 0, block; block = blocks[i]; i++) { block.moveBy(offset, 0); From 51d9981fe3ccc654dac58e8622b163a76ad3a11c Mon Sep 17 00:00:00 2001 From: Rodrigo Queiro Date: Wed, 11 May 2016 16:52:51 -0700 Subject: [PATCH 25/32] Fix typo in workspacetoCode --- core/generator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/generator.js b/core/generator.js index 48378cd4..3cab1ccf 100644 --- a/core/generator.js +++ b/core/generator.js @@ -77,7 +77,7 @@ Blockly.Generator.prototype.INDENT = ' '; */ Blockly.Generator.prototype.workspaceToCode = function(workspace) { if (!workspace) { - // Backwards compatability from before there could be multiple workspaces. + // Backwards compatibility from before there could be multiple workspaces. console.warn('No workspace specified in workspaceToCode call. Guessing.'); workspace = Blockly.getMainWorkspace(); } From 5f1568ef60733fb7ffcfbc874ba27ae4de54a121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20Laxstr=C3=B6m?= Date: Thu, 12 May 2016 08:55:38 +0200 Subject: [PATCH 26/32] Localisation updates from https://translatewiki.net. --- msg/json/de.json | 8 ++++++++ msg/json/es.json | 13 ++++++++++++- msg/json/fr.json | 8 ++++++++ msg/json/he.json | 8 +++++++- msg/json/it.json | 7 +++++++ msg/json/pms.json | 4 ++++ msg/json/qqq.json | 13 ++++++++++--- msg/json/ru.json | 5 +++++ msg/json/sv.json | 8 ++++++++ msg/json/zh-hans.json | 8 ++++++++ msg/json/zh-hant.json | 2 +- 11 files changed, 78 insertions(+), 6 deletions(-) diff --git a/msg/json/de.json b/msg/json/de.json index d94a0dc0..da15c983 100644 --- a/msg/json/de.json +++ b/msg/json/de.json @@ -302,6 +302,14 @@ "LISTS_GET_SUBLIST_END_LAST": "bis zum Ende", "LISTS_GET_SUBLIST_TAIL": "", "LISTS_GET_SUBLIST_TOOLTIP": "Erstellt eine Kopie mit dem angegebenen Abschnitt der Liste.", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", + "LISTS_SORT_TITLE": "%1 %2 %3 sortieren", + "LISTS_SORT_TOOLTIP": "Eine Kopie einer Liste sortieren.", + "LISTS_SORT_ORDER_ASCENDING": "aufsteigend", + "LISTS_SORT_ORDER_DESCENDING": "absteigend", + "LISTS_SORT_TYPE_NUMERIC": "numerisch", + "LISTS_SORT_TYPE_TEXT": "alphabetisch", + "LISTS_SORT_TYPE_IGNORECASE": "alphabetisch, Schreibung ignorieren", "LISTS_SPLIT_HELPURL": "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists", "LISTS_SPLIT_LIST_FROM_TEXT": "Liste aus Text erstellen", "LISTS_SPLIT_TEXT_FROM_LIST": "Text aus Liste erstellen", diff --git a/msg/json/es.json b/msg/json/es.json index fab4123b..52902f14 100644 --- a/msg/json/es.json +++ b/msg/json/es.json @@ -4,7 +4,10 @@ "Fitoschido", "VegaDark", "WeSiToS", - "Macofe" + "Macofe", + "Codynguyen1116", + "Indiralena", + "Rubentl134" ] }, "VARIABLES_DEFAULT_NAME": "elemento", @@ -276,6 +279,14 @@ "LISTS_GET_SUBLIST_END_FROM_END": "hasta # del final", "LISTS_GET_SUBLIST_END_LAST": "hasta el último", "LISTS_GET_SUBLIST_TOOLTIP": "Crea una copia de la parte especificada de una lista.", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", + "LISTS_SORT_TITLE": "tipo %1 %2 %3", + "LISTS_SORT_TOOLTIP": "Ordenar una copia de una lista.", + "LISTS_SORT_ORDER_ASCENDING": "ascendente", + "LISTS_SORT_ORDER_DESCENDING": "descendente", + "LISTS_SORT_TYPE_NUMERIC": "numérico", + "LISTS_SORT_TYPE_TEXT": "alfabético", + "LISTS_SORT_TYPE_IGNORECASE": "alfabético, ignorar tamaño", "LISTS_SPLIT_HELPURL": "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists", "LISTS_SPLIT_LIST_FROM_TEXT": "hacer lista a partir de texto", "LISTS_SPLIT_TEXT_FROM_LIST": "hacer texto a partir de lista", diff --git a/msg/json/fr.json b/msg/json/fr.json index 03f90d53..e4d0b7d7 100644 --- a/msg/json/fr.json +++ b/msg/json/fr.json @@ -283,6 +283,14 @@ "LISTS_GET_SUBLIST_END_FROM_END": "jusqu’à # depuis la fin", "LISTS_GET_SUBLIST_END_LAST": "jusqu’à la fin", "LISTS_GET_SUBLIST_TOOLTIP": "Crée une copie de la partie spécifiée d’une liste.", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", + "LISTS_SORT_TITLE": "trier %1 %2 %3", + "LISTS_SORT_TOOLTIP": "Trier une copie d’une liste.", + "LISTS_SORT_ORDER_ASCENDING": "croissant", + "LISTS_SORT_ORDER_DESCENDING": "décroissant", + "LISTS_SORT_TYPE_NUMERIC": "numérique", + "LISTS_SORT_TYPE_TEXT": "alphabétique", + "LISTS_SORT_TYPE_IGNORECASE": "alphabétique, en ignorant la casse", "LISTS_SPLIT_HELPURL": "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists", "LISTS_SPLIT_LIST_FROM_TEXT": "créer une liste depuis le texte", "LISTS_SPLIT_TEXT_FROM_LIST": "créer un texte depuis la liste", diff --git a/msg/json/he.json b/msg/json/he.json index deae1483..546e88e3 100644 --- a/msg/json/he.json +++ b/msg/json/he.json @@ -10,7 +10,8 @@ "Dvb", "LaG roiL", "아라", - "Elyashiv" + "Elyashiv", + "Guycn2" ] }, "VARIABLES_DEFAULT_NAME": "פריט", @@ -241,6 +242,11 @@ "LISTS_GET_SUBLIST_END_FROM_END": "ל # מהסוף", "LISTS_GET_SUBLIST_END_LAST": "לאחרון", "LISTS_GET_SUBLIST_TOOLTIP": "יוצרת עותק של חלק מסוים מהרשימה.", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", + "LISTS_SORT_ORDER_ASCENDING": "סדר עולה", + "LISTS_SORT_ORDER_DESCENDING": "סדר יורד", + "LISTS_SORT_TYPE_TEXT": "סדר אלפביתי", + "LISTS_SORT_TYPE_IGNORECASE": "סדר אלפביתי, לא תלוי רישיות", "LISTS_SPLIT_LIST_FROM_TEXT": "יצירת רשימה מטקסט", "LISTS_SPLIT_TEXT_FROM_LIST": "יצירת טקסט מרשימה", "VARIABLES_GET_TOOLTIP": "להחזיר את הערך של משתנה זה.", diff --git a/msg/json/it.json b/msg/json/it.json index 70d7faf3..2c88866e 100644 --- a/msg/json/it.json +++ b/msg/json/it.json @@ -277,6 +277,13 @@ "LISTS_GET_SUBLIST_END_FROM_END": "da # dalla fine", "LISTS_GET_SUBLIST_END_LAST": "dagli ultimi", "LISTS_GET_SUBLIST_TOOLTIP": "Crea una copia della porzione specificata di una lista.", + "LISTS_SORT_TITLE": "ordinamento %1 %2 %3", + "LISTS_SORT_TOOLTIP": "Ordina una copia di un elenco.", + "LISTS_SORT_ORDER_ASCENDING": "crescente", + "LISTS_SORT_ORDER_DESCENDING": "decrescente", + "LISTS_SORT_TYPE_NUMERIC": "numerico", + "LISTS_SORT_TYPE_TEXT": "alfabetico", + "LISTS_SORT_TYPE_IGNORECASE": "alfabetico, ignorare differenze maiuscole e minuscole", "LISTS_SPLIT_LIST_FROM_TEXT": "crea lista da testo", "LISTS_SPLIT_TEXT_FROM_LIST": "crea testo da lista", "LISTS_SPLIT_WITH_DELIMITER": "con delimitatore", diff --git a/msg/json/pms.json b/msg/json/pms.json index ce21a155..041e739c 100644 --- a/msg/json/pms.json +++ b/msg/json/pms.json @@ -272,6 +272,10 @@ "LISTS_GET_SUBLIST_END_FROM_END": "fin-a a # da la fin", "LISTS_GET_SUBLIST_END_LAST": "fin-a a l'ùltim", "LISTS_GET_SUBLIST_TOOLTIP": "A crea na còpia dël tòch ëspessificà ëd na lista.", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", + "LISTS_SORT_TITLE": "ordiné %1 %2 %3", + "LISTS_SORT_TOOLTIP": "Ordiné na còpia ëd na lista.", + "LISTS_SORT_ORDER_ASCENDING": "chërsent", "LISTS_SPLIT_HELPURL": "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists", "LISTS_SPLIT_LIST_FROM_TEXT": "fé na lista da 'n test", "LISTS_SPLIT_TEXT_FROM_LIST": "fé 'n test da na lista", diff --git a/msg/json/qqq.json b/msg/json/qqq.json index 179f12a6..c0a559ae 100644 --- a/msg/json/qqq.json +++ b/msg/json/qqq.json @@ -1,4 +1,11 @@ { + "@metadata": { + "authors": [ + "Espertus", + "Liuxinyu970226", + "Shirayuki" + ] + }, "VARIABLES_DEFAULT_NAME": "default name - A simple, general default name for a variable, preferably short. For more context, see [[Translating:Blockly#infrequent_message_types]].\n{{Identical|Item}}", "TODAY": "button text - Botton that sets a calendar to today's date.\n{{Identical|Today}}", "DUPLICATE_BLOCK": "context menu - Make a copy of the selected block (and any blocks it contains).\n{{Identical|Duplicate}}", @@ -96,7 +103,7 @@ "LOGIC_BOOLEAN_FALSE": "block text - The word for the [https://en.wikipedia.org/wiki/Truth_value logical value] ''false''.\n{{Identical|False}}", "LOGIC_BOOLEAN_TOOLTIP": "tooltip - Indicates that the block returns either of the two possible [https://en.wikipedia.org/wiki/Truth_value logical values].", "LOGIC_NULL_HELPURL": "url - Provide a link to the translation of [https://en.wikipedia.org/wiki/Nullable_type https://en.wikipedia.org/wiki/Nullable_type], if it exists in your language; otherwise, do not worry about translating this advanced concept.", - "LOGIC_NULL": "block text - In computer languages, ''null'' is a special value that indicates that no value has been set. You may use your language's word for 'nothing' or 'invalid'.", + "LOGIC_NULL": "block text - In computer languages, ''null'' is a special value that indicates that no value has been set. You may use your language's word for 'nothing' or 'invalid'.\n{{Identical|Null}}", "LOGIC_NULL_TOOLTIP": "tooltip - This should use the word from the previous message.", "LOGIC_TERNARY_HELPURL": "url - Describes the programming language operator known as the ''ternary'' or ''conditional'' operator. It is recommended that you use the translation of [https://en.wikipedia.org/wiki/%3F: https://en.wikipedia.org/wiki/%3F:] if it exists.", "LOGIC_TERNARY_CONDITION": "block input text - Label for the input whose value determines which of the other two inputs is returned. In some programming languages, this is called a ''''predicate''''.", @@ -320,8 +327,8 @@ "LISTS_SORT_HELPURL": "url - Information describing sorting a list.", "LISTS_SORT_TITLE": "Sort as type %1 (numeric or alphabetic) in order %2 (ascending or descending) a list of items %3.", "LISTS_SORT_TOOLTIP": "tooltip - See [https://github.com/google/blockly/wiki/Lists#sorting-a-list].", - "LISTS_SORT_ORDER_ASCENDING": "sorting order or direction from low to high value for numeric, or A-Z for alphabetic.", - "LISTS_SORT_ORDER_DESCENDING": "sorting order or direction from high to low value for numeric, or Z-A for alphabetic.", + "LISTS_SORT_ORDER_ASCENDING": "sorting order or direction from low to high value for numeric, or A-Z for alphabetic.\n{{Identical|Ascending}}", + "LISTS_SORT_ORDER_DESCENDING": "sorting order or direction from high to low value for numeric, or Z-A for alphabetic.\n{{Identical|Descending}}", "LISTS_SORT_TYPE_NUMERIC": "sort by treating each item as a number.", "LISTS_SORT_TYPE_TEXT": "sort by treating each item alphabetically, case-sensitive.", "LISTS_SORT_TYPE_IGNORECASE": "sort by treating each item alphabetically, ignoring differences in case.", diff --git a/msg/json/ru.json b/msg/json/ru.json index 50fa9d30..59c528e6 100644 --- a/msg/json/ru.json +++ b/msg/json/ru.json @@ -277,6 +277,11 @@ "LISTS_GET_SUBLIST_END_FROM_END": "по № с конца", "LISTS_GET_SUBLIST_END_LAST": "по последний", "LISTS_GET_SUBLIST_TOOLTIP": "Создаёт копию указанной части списка.", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", + "LISTS_SORT_TITLE": "сортировать %1 %2 %3", + "LISTS_SORT_TOOLTIP": "Сортировать копию списка.", + "LISTS_SORT_ORDER_ASCENDING": "по возрастанию", + "LISTS_SORT_ORDER_DESCENDING": "по убыванию", "LISTS_SPLIT_HELPURL": "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists", "LISTS_SPLIT_LIST_FROM_TEXT": "сделать список из текста", "LISTS_SPLIT_TEXT_FROM_LIST": "собрать текст из списка", diff --git a/msg/json/sv.json b/msg/json/sv.json index 4940f0d6..ed8acb30 100644 --- a/msg/json/sv.json +++ b/msg/json/sv.json @@ -293,6 +293,14 @@ "LISTS_GET_SUBLIST_END_FROM_END": "till # från slutet", "LISTS_GET_SUBLIST_END_LAST": "till sista", "LISTS_GET_SUBLIST_TOOLTIP": "Skapar en kopia av den specificerade delen av en lista.", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", + "LISTS_SORT_TITLE": "sortera %1 %2 %3", + "LISTS_SORT_TOOLTIP": "Sortera en kopia av en lista.", + "LISTS_SORT_ORDER_ASCENDING": "stigande", + "LISTS_SORT_ORDER_DESCENDING": "fallande", + "LISTS_SORT_TYPE_NUMERIC": "numeriskt", + "LISTS_SORT_TYPE_TEXT": "alfabetiskt", + "LISTS_SORT_TYPE_IGNORECASE": "alfabetiskt, ignorera skiftläge", "LISTS_SPLIT_HELPURL": "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists", "LISTS_SPLIT_LIST_FROM_TEXT": "skapa lista från text", "LISTS_SPLIT_TEXT_FROM_LIST": "skapa text från lista", diff --git a/msg/json/zh-hans.json b/msg/json/zh-hans.json index 0698b511..67b58d99 100644 --- a/msg/json/zh-hans.json +++ b/msg/json/zh-hans.json @@ -288,6 +288,14 @@ "LISTS_GET_SUBLIST_END_LAST": "到最后", "LISTS_GET_SUBLIST_TAIL": "空白", "LISTS_GET_SUBLIST_TOOLTIP": "复制列表中指定的部分。", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", + "LISTS_SORT_TITLE": "排序%1 %2 %3", + "LISTS_SORT_TOOLTIP": "排序一个列表的拷贝。", + "LISTS_SORT_ORDER_ASCENDING": "升序", + "LISTS_SORT_ORDER_DESCENDING": "降序", + "LISTS_SORT_TYPE_NUMERIC": "按数字排序", + "LISTS_SORT_TYPE_TEXT": "按字母排序", + "LISTS_SORT_TYPE_IGNORECASE": "按字母排序,忽略大小写", "LISTS_SPLIT_HELPURL": "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists", "LISTS_SPLIT_LIST_FROM_TEXT": "从文本制作列表", "LISTS_SPLIT_TEXT_FROM_LIST": "从列表拆出文本", diff --git a/msg/json/zh-hant.json b/msg/json/zh-hant.json index 53ebbf51..5b781424 100644 --- a/msg/json/zh-hant.json +++ b/msg/json/zh-hant.json @@ -120,7 +120,7 @@ "MATH_SINGLE_TOOLTIP_LOG10": "返回指定數字的對數。", "MATH_SINGLE_TOOLTIP_EXP": "返回指定數字指數的 e", "MATH_SINGLE_TOOLTIP_POW10": "返回指定數字指數的10的冪次。", - "MATH_TRIG_HELPURL": "https://en.wikipedia.org/wiki/Trigonometric_functions", + "MATH_TRIG_HELPURL": "https://zh.wikipedia.org/wiki/三角函數", "MATH_TRIG_TOOLTIP_SIN": "返回指定角度的正弦值(非弧度)。", "MATH_TRIG_TOOLTIP_COS": "返回指定角度的餘弦值(非弧度)。", "MATH_TRIG_TOOLTIP_TAN": "返回指定角度的正切值(非弧度)。", From 666536807a717b24214e268b20e506c78054a15b Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 13 May 2016 14:26:32 -0700 Subject: [PATCH 27/32] Add comments; scroll to start for always open flyout --- core/flyout.js | 6 +++--- core/inject.js | 1 + tests/multi_playground.html | 5 +++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/flyout.js b/core/flyout.js index 80a5e0de..f980ce19 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -872,8 +872,8 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(originBlock) { var scale = this.workspace_.scale; xyOld.x += scrollX / scale - scrollX; // If the flyout is on the right side, (0, 0) in the flyout is offset to - // the right of (0, 0) in the main workspace. Offset to take that into - // account. + // the right of (0, 0) in the main workspace. Add an offset to take that + // into account. if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { scrollX = targetWorkspace.getMetrics().viewWidth - this.width_; scale = targetWorkspace.scale; @@ -889,7 +889,7 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(originBlock) { scale = this.workspace_.scale; xyOld.y += scrollY / scale - scrollY; // If the flyout is on the bottom, (0, 0) in the flyout is offset to be below - // (0, 0) in the main workspace. Offset to take that into account. + // (0, 0) in the main workspace. Add an offset to take that into account. if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { scrollY = targetWorkspace.getMetrics().viewHeight - this.height_; scale = targetWorkspace.scale; diff --git a/core/inject.js b/core/inject.js index 419974c2..17bfccc0 100644 --- a/core/inject.js +++ b/core/inject.js @@ -278,6 +278,7 @@ Blockly.init_ = function(mainWorkspace) { // Build a fixed flyout with the root blocks. mainWorkspace.flyout_.init(mainWorkspace); mainWorkspace.flyout_.show(options.languageTree.childNodes); + mainWorkspace.flyout_.scrollToStart(); // Translate the workspace sideways to avoid the fixed flyout. mainWorkspace.scrollX = mainWorkspace.flyout_.width_; if (options.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { diff --git a/tests/multi_playground.html b/tests/multi_playground.html index 8162b02c..9cf4d3d7 100644 --- a/tests/multi_playground.html +++ b/tests/multi_playground.html @@ -153,6 +153,11 @@ h1 { + + + + +