mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-08-28 22:10:31 -04:00
Merge pull request #52 from rachel-fenichel/feature/horizontal_flyout
Horizontal flyout rendering
This commit is contained in:
commit
0ccdf83e18
3 changed files with 274 additions and 126 deletions
392
core/flyout.js
392
core/flyout.js
|
@ -56,6 +56,12 @@ Blockly.Flyout = function(workspaceOptions) {
|
|||
*/
|
||||
this.RTL = !!workspaceOptions.RTL;
|
||||
|
||||
/**
|
||||
* Flyout should be laid out horizontally vs vertically.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.horizontalLayout_ = false;
|
||||
|
||||
/**
|
||||
* Opaque data that can be passed to Blockly.unbindEvent_.
|
||||
* @type {!Array.<!Array>}
|
||||
|
@ -148,7 +154,7 @@ 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();
|
||||
|
||||
|
@ -159,7 +165,7 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) {
|
|||
Blockly.bindEvent_(this.targetWorkspace_.getCanvas(),
|
||||
'blocklyWorkspaceChange', this, this.filterForCapacity_));
|
||||
}
|
||||
// Dragging the flyout up and down.
|
||||
// Dragging the flyout up and down (or left and right).
|
||||
Array.prototype.push.apply(this.eventWrappers_,
|
||||
Blockly.bindEvent_(this.svgGroup_, 'mousedown', this, this.onMouseDown_));
|
||||
};
|
||||
|
@ -194,9 +200,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
|
||||
|
@ -206,42 +215,64 @@ 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 {
|
||||
|
||||
if (this.horizontalLayout_) {
|
||||
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,
|
||||
contentWidth: (optionBox.width) * this.workspace_.scale,
|
||||
viewTop: -this.workspace_.scrollY,
|
||||
contentTop: 0,
|
||||
viewLeft: -this.workspace_.scrollX,
|
||||
contentTop: optionBox.y,
|
||||
contentLeft: 0,
|
||||
absoluteTop: this.verticalOffset_ + this.SCROLLBAR_PADDING,
|
||||
absoluteLeft: 0
|
||||
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);
|
||||
this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft,
|
||||
this.workspace_.scrollY + metrics.absoluteTop);
|
||||
};
|
||||
|
||||
Blockly.Flyout.prototype.setVerticalOffset = function(verticalOffset) {
|
||||
|
@ -260,24 +291,14 @@ Blockly.Flyout.prototype.position = function() {
|
|||
// Hidden components will return null.
|
||||
return;
|
||||
}
|
||||
var edgeWidth = this.width_ - this.CORNER_RADIUS;
|
||||
var edgeWidth = this.horizontalLayout_ ? metrics.viewWidth : this.width_;
|
||||
edgeWidth -= this.CORNER_RADIUS;
|
||||
if (this.RTL) {
|
||||
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) {
|
||||
|
@ -287,8 +308,13 @@ Blockly.Flyout.prototype.position = function() {
|
|||
this.svgGroup_.setAttribute('transform',
|
||||
'translate(' + x + ',' + metrics.absoluteTop + ')');
|
||||
|
||||
// Record the height for Blockly.Flyout.getMetrics_.
|
||||
this.height_ = metrics.viewHeight;
|
||||
// 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_) {
|
||||
|
@ -296,10 +322,41 @@ 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
|
||||
* rounded corners.
|
||||
* @param {number} height The height of the toolbox, not including
|
||||
* rounded corners.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.setBackgroundPath_ = function(width, height) {
|
||||
// Decide whether to start on the left or right.
|
||||
var path = ['M ' + (this.RTL ? this.width_ : 0) + ',0'];
|
||||
// Top.
|
||||
path.push('h', width);
|
||||
// Rounded corner.
|
||||
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);
|
||||
// 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,
|
||||
this.RTL ? 0 : 1,
|
||||
this.RTL ? this.CORNER_RADIUS : -this.CORNER_RADIUS,
|
||||
this.CORNER_RADIUS);
|
||||
// Bottom.
|
||||
path.push('h', -width);
|
||||
path.push('z');
|
||||
this.svgBackground_.setAttribute('d', path.join(' '));
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the flyout to the top.
|
||||
*/
|
||||
Blockly.Flyout.prototype.scrollToTop = function() {
|
||||
Blockly.Flyout.prototype.scrollToStart = function() {
|
||||
this.scrollbar_.set(0);
|
||||
};
|
||||
|
||||
|
@ -309,6 +366,10 @@ Blockly.Flyout.prototype.scrollToTop = 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) {
|
||||
|
@ -401,7 +462,11 @@ Blockly.Flyout.prototype.show = function(xmlList) {
|
|||
}
|
||||
}
|
||||
|
||||
// Lay out the blocks vertically.
|
||||
// Lay out the blocks.
|
||||
var cursorX = margin / this.workspace_.scale + Blockly.BlockSvg.TAB_WIDTH;
|
||||
if (this.RTL) {
|
||||
cursorX = this.width_ - cursorX;
|
||||
}
|
||||
var cursorY = margin;
|
||||
for (var i = 0, block; block = blocks[i]; i++) {
|
||||
var allBlocks = block.getDescendants();
|
||||
|
@ -414,10 +479,16 @@ 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(cursorX, cursorY);
|
||||
if (this.horizontalLayout_) {
|
||||
if (this.RTL) {
|
||||
cursorX -= (blockHW.width + gaps[i]);
|
||||
} else {
|
||||
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.
|
||||
|
@ -427,6 +498,43 @@ 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_();
|
||||
|
||||
this.reflowWrapper_ = Blockly.bindEvent_(this.workspace_.getCanvas(),
|
||||
'blocklyWorkspaceChange', this, this.reflow);
|
||||
this.workspace_.fireChangeEvent();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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)));
|
||||
|
@ -444,77 +552,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_ = Blockly.bindEvent_(this.workspace_.getCanvas(),
|
||||
'blocklyWorkspaceChange', this, this.reflow);
|
||||
this.workspace_.fireChangeEvent();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -550,7 +587,7 @@ Blockly.Flyout.prototype.blockMouseDown_ = function(block) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Mouse down on the flyout background. Start a vertical scroll drag.
|
||||
* Mouse down on the flyout background. Start a scroll drag.
|
||||
* @param {!Event} e Mouse down event.
|
||||
* @private
|
||||
*/
|
||||
|
@ -561,6 +598,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',
|
||||
|
@ -571,18 +609,28 @@ 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -699,14 +747,21 @@ Blockly.Flyout.prototype.getRect = function() {
|
|||
// but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE).
|
||||
var BIG_NUM = 1000000000;
|
||||
var mainWorkspace = Blockly.mainWorkspace;
|
||||
var x = Blockly.getSvgXY_(this.svgGroup_, mainWorkspace).x;
|
||||
if (!this.RTL) {
|
||||
x -= BIG_NUM;
|
||||
}
|
||||
|
||||
// Fix scale if nested in zoomed workspace.
|
||||
var scale = this.targetWorkspace_ == mainWorkspace ? 1 : mainWorkspace.scale;
|
||||
return new goog.math.Rect(x, -BIG_NUM,
|
||||
BIG_NUM + this.width_ * scale, BIG_NUM * 2);
|
||||
var x = Blockly.getSvgXY_(this.svgGroup_, mainWorkspace).x;
|
||||
if (this.horizontalLayout_) {
|
||||
var y = Blockly.getSvgXY_(this.svgGroup_, mainWorkspace).y - BIG_NUM;
|
||||
return new goog.math.Rect(-BIG_NUM, y, BIG_NUM * 2,
|
||||
BIG_NUM + this.height_ * scale);
|
||||
} else {
|
||||
if (!this.RTL) {
|
||||
x -= BIG_NUM;
|
||||
}
|
||||
return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + this.width_ * scale,
|
||||
BIG_NUM * 2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -734,3 +789,94 @@ 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 += Blockly.BlockSvg.TAB_WIDTH;
|
||||
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');
|
||||
}
|
||||
|
|
|
@ -497,8 +497,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 ratio = this.ratio_ == this.ratio_ || 0;
|
||||
// Move the scrollbar slider.
|
||||
this.svgKnob_.setAttribute(this.horizontal_ ? 'x' : 'y', value * this.ratio_);
|
||||
this.svgKnob_.setAttribute(this.horizontal_ ? 'x' : 'y', value * ratio);
|
||||
this.onScroll_();
|
||||
};
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ Blockly.Toolbox = function(workspace) {
|
|||
* @private
|
||||
*/
|
||||
this.horizontalLayout_ = false;
|
||||
|
||||
if (this.horizontalLayout_) {
|
||||
this.CONFIG_['cssTreeRow'] =
|
||||
this.CONFIG_['cssTreeRow']
|
||||
|
@ -449,9 +450,9 @@ Blockly.Toolbox.TreeControl.prototype.setSelectedItem = function(node) {
|
|||
goog.ui.tree.TreeControl.prototype.setSelectedItem.call(this, node);
|
||||
if (node && node.blocks && node.blocks.length) {
|
||||
toolbox.flyout_.show(node.blocks);
|
||||
// Scroll the flyout to the top if the category has changed.
|
||||
// Scroll the flyout to the start if the category has changed.
|
||||
if (toolbox.lastCategory_ != node) {
|
||||
toolbox.flyout_.scrollToTop();
|
||||
toolbox.flyout_.scrollToStart();
|
||||
}
|
||||
} else {
|
||||
// Hide the flyout.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue