Commit 0a191c1c authored by Neil Fraser's avatar Neil Fraser

Convert Blockly.Toolbox from a singleton to an instance.

parent 674625c4
This diff is collapsed.
...@@ -58,7 +58,7 @@ goog.addDependency("../../../" + dir + "/core/toolbox.js", ['Blockly.Toolbox'], ...@@ -58,7 +58,7 @@ goog.addDependency("../../../" + dir + "/core/toolbox.js", ['Blockly.Toolbox'],
goog.addDependency("../../../" + dir + "/core/tooltip.js", ['Blockly.Tooltip'], []); goog.addDependency("../../../" + dir + "/core/tooltip.js", ['Blockly.Tooltip'], []);
goog.addDependency("../../../" + dir + "/core/trashcan.js", ['Blockly.Trashcan'], ['goog.math', 'goog.math.Rect', 'goog.Timer']); goog.addDependency("../../../" + dir + "/core/trashcan.js", ['Blockly.Trashcan'], ['goog.math', 'goog.math.Rect', 'goog.Timer']);
goog.addDependency("../../../" + dir + "/core/utils.js", ['Blockly.utils'], []); goog.addDependency("../../../" + dir + "/core/utils.js", ['Blockly.utils'], []);
goog.addDependency("../../../" + dir + "/core/variables.js", ['Blockly.Variables'], ['Blockly.Toolbox', 'Blockly.Workspace']); goog.addDependency("../../../" + dir + "/core/variables.js", ['Blockly.Variables'], ['Blockly.Workspace']);
goog.addDependency("../../../" + dir + "/core/warning.js", ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Icon']); goog.addDependency("../../../" + dir + "/core/warning.js", ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Icon']);
goog.addDependency("../../../" + dir + "/core/widgetdiv.js", ['Blockly.WidgetDiv'], ['Blockly.Css', 'goog.dom']); goog.addDependency("../../../" + dir + "/core/widgetdiv.js", ['Blockly.WidgetDiv'], ['Blockly.Css', 'goog.dom']);
goog.addDependency("../../../" + dir + "/core/workspace.js", ['Blockly.Workspace'], ['Blockly.ScrollbarPair', 'Blockly.Trashcan', 'Blockly.Xml', 'goog.math', 'goog.math.Coordinate']); goog.addDependency("../../../" + dir + "/core/workspace.js", ['Blockly.Workspace'], ['Blockly.ScrollbarPair', 'Blockly.Trashcan', 'Blockly.Xml', 'goog.math', 'goog.math.Coordinate']);
......
...@@ -512,8 +512,10 @@ Blockly.hideChaff = function(opt_allowToolbox) { ...@@ -512,8 +512,10 @@ Blockly.hideChaff = function(opt_allowToolbox) {
Blockly.Tooltip.hide(); Blockly.Tooltip.hide();
Blockly.WidgetDiv.hide(); Blockly.WidgetDiv.hide();
if (!opt_allowToolbox && if (!opt_allowToolbox &&
Blockly.Toolbox.flyout_ && Blockly.Toolbox.flyout_.autoClose) { Blockly.mainWorkspace.toolbox_ &&
Blockly.Toolbox.clearSelection(); Blockly.mainWorkspace.toolbox_.flyout_ &&
Blockly.mainWorkspace.toolbox_.flyout_.autoClose) {
Blockly.mainWorkspace.toolbox_.clearSelection();
} }
}; };
...@@ -638,7 +640,9 @@ Blockly.playAudio = function(name, opt_volume) { ...@@ -638,7 +640,9 @@ Blockly.playAudio = function(name, opt_volume) {
*/ */
Blockly.getMainWorkspaceMetrics_ = function() { Blockly.getMainWorkspaceMetrics_ = function() {
var svgSize = Blockly.svgSize(); var svgSize = Blockly.svgSize();
svgSize.width -= Blockly.Toolbox.width; // Zero if no Toolbox. if (Blockly.mainWorkspace.toolbox_) {
svgSize.width -= Blockly.mainWorkspace.toolbox_.width;
}
var viewWidth = svgSize.width - Blockly.Scrollbar.scrollbarThickness; var viewWidth = svgSize.width - Blockly.Scrollbar.scrollbarThickness;
var viewHeight = svgSize.height - Blockly.Scrollbar.scrollbarThickness; var viewHeight = svgSize.height - Blockly.Scrollbar.scrollbarThickness;
try { try {
...@@ -664,7 +668,10 @@ Blockly.getMainWorkspaceMetrics_ = function() { ...@@ -664,7 +668,10 @@ Blockly.getMainWorkspaceMetrics_ = function() {
var topEdge = blockBox.y; var topEdge = blockBox.y;
var bottomEdge = topEdge + blockBox.height; var bottomEdge = topEdge + blockBox.height;
} }
var absoluteLeft = Blockly.RTL ? 0 : Blockly.Toolbox.width; var absoluteLeft = 0;
if (!Blockly.RTL && Blockly.mainWorkspace.toolbox_) {
absoluteLeft = Blockly.mainWorkspace.toolbox_.width;
}
var metrics = { var metrics = {
viewHeight: svgSize.height, viewHeight: svgSize.height,
viewWidth: svgSize.width, viewWidth: svgSize.width,
......
...@@ -275,7 +275,7 @@ Blockly.createDom_ = function(container) { ...@@ -275,7 +275,7 @@ Blockly.createDom_ = function(container) {
// Determine if there needs to be a category tree, or a simple list of // Determine if there needs to be a category tree, or a simple list of
// blocks. This cannot be changed later, since the UI is very different. // blocks. This cannot be changed later, since the UI is very different.
if (Blockly.hasCategories) { if (Blockly.hasCategories) {
Blockly.Toolbox.createDom(svg, container); Blockly.mainWorkspace.toolbox_ = new Blockly.Toolbox(svg, container);
} else { } else {
/** /**
* @type {!Blockly.Flyout} * @type {!Blockly.Flyout}
...@@ -384,9 +384,9 @@ Blockly.init_ = function() { ...@@ -384,9 +384,9 @@ Blockly.init_ = function() {
} }
if (Blockly.languageTree) { if (Blockly.languageTree) {
if (Blockly.hasCategories) { if (Blockly.mainWorkspace.toolbox_) {
Blockly.Toolbox.init(); Blockly.mainWorkspace.toolbox_.init();
} else { } else if (Blockly.mainWorkspace.flyout_) {
// Build a fixed flyout with the root blocks. // Build a fixed flyout with the root blocks.
Blockly.mainWorkspace.flyout_.init(Blockly.mainWorkspace); Blockly.mainWorkspace.flyout_.init(Blockly.mainWorkspace);
Blockly.mainWorkspace.flyout_.show(Blockly.languageTree.childNodes); Blockly.mainWorkspace.flyout_.show(Blockly.languageTree.childNodes);
...@@ -458,7 +458,7 @@ Blockly.updateToolbox = function(tree) { ...@@ -458,7 +458,7 @@ Blockly.updateToolbox = function(tree) {
throw 'Existing toolbox has no categories. Can\'t change mode.'; throw 'Existing toolbox has no categories. Can\'t change mode.';
} }
Blockly.languageTree = tree; Blockly.languageTree = tree;
Blockly.Toolbox.populate_(); Blockly.mainWorkspace.toolbox_.populate_();
} else { } else {
if (Blockly.hasCategories) { if (Blockly.hasCategories) {
throw 'Existing toolbox has categories. Can\'t change mode.'; throw 'Existing toolbox has categories. Can\'t change mode.';
......
...@@ -34,18 +34,51 @@ goog.require('goog.ui.tree.TreeControl'); ...@@ -34,18 +34,51 @@ goog.require('goog.ui.tree.TreeControl');
goog.require('goog.ui.tree.TreeNode'); goog.require('goog.ui.tree.TreeNode');
/**
* Class for a Toolbox.
* Creates the toolbox's DOM. Only needs to be called once.
* @param {!Element} svg The top-level SVG element.
* @param {!Element} container The SVG's HTML parent element.
* @constructor
*/
Blockly.Toolbox = function(svg, container) {
// Create an HTML container for the Toolbox menu.
this.HtmlDiv = goog.dom.createDom('div', 'blocklyToolboxDiv');
this.HtmlDiv.setAttribute('dir', Blockly.RTL ? 'RTL' : 'LTR');
container.appendChild(this.HtmlDiv);
/**
* @type {!Blockly.Flyout}
* @private
*/
this.flyout_ = new Blockly.Flyout();
svg.appendChild(this.flyout_.createDom());
// Clicking on toolbar closes popups.
Blockly.bindEvent_(this.HtmlDiv, 'mousedown', this,
function(e) {
if (Blockly.isRightButton(e) || e.target == this.HtmlDiv) {
// Close flyout.
Blockly.hideChaff(false);
} else {
// Just close popups.
Blockly.hideChaff(true);
}
});
};
/** /**
* Width of the toolbox. * Width of the toolbox.
* @type {number} * @type {number}
*/ */
Blockly.Toolbox.width = 0; Blockly.Toolbox.prototype.width = 0;
/** /**
* The SVG group currently selected. * The SVG group currently selected.
* @type {SVGGElement} * @type {SVGGElement}
* @private * @private
*/ */
Blockly.Toolbox.selectedOption_ = null; Blockly.Toolbox.prototype.selectedOption_ = null;
/** /**
* Configuration constants for Closure's tree UI. * Configuration constants for Closure's tree UI.
...@@ -53,7 +86,7 @@ Blockly.Toolbox.selectedOption_ = null; ...@@ -53,7 +86,7 @@ Blockly.Toolbox.selectedOption_ = null;
* @const * @const
* @private * @private
*/ */
Blockly.Toolbox.CONFIG_ = { Blockly.Toolbox.prototype.CONFIG_ = {
indentWidth: 19, indentWidth: 19,
cssRoot: 'blocklyTreeRoot', cssRoot: 'blocklyTreeRoot',
cssHideRoot: 'blocklyHidden', cssHideRoot: 'blocklyHidden',
...@@ -66,70 +99,38 @@ Blockly.Toolbox.CONFIG_ = { ...@@ -66,70 +99,38 @@ Blockly.Toolbox.CONFIG_ = {
cssSelectedRow: 'blocklyTreeSelected' cssSelectedRow: 'blocklyTreeSelected'
}; };
/**
* Creates the toolbox's DOM. Only needs to be called once.
* @param {!Element} svg The top-level SVG element.
* @param {!Element} container The SVG's HTML parent element.
*/
Blockly.Toolbox.createDom = function(svg, container) {
// Create an HTML container for the Toolbox menu.
Blockly.Toolbox.HtmlDiv = goog.dom.createDom('div', 'blocklyToolboxDiv');
Blockly.Toolbox.HtmlDiv.setAttribute('dir', Blockly.RTL ? 'RTL' : 'LTR');
container.appendChild(Blockly.Toolbox.HtmlDiv);
/**
* @type {!Blockly.Flyout}
* @private
*/
Blockly.Toolbox.flyout_ = new Blockly.Flyout();
svg.appendChild(Blockly.Toolbox.flyout_.createDom());
// Clicking on toolbar closes popups.
Blockly.bindEvent_(Blockly.Toolbox.HtmlDiv, 'mousedown', null,
function(e) {
if (Blockly.isRightButton(e) || e.target == Blockly.Toolbox.HtmlDiv) {
// Close flyout.
Blockly.hideChaff(false);
} else {
// Just close popups.
Blockly.hideChaff(true);
}
});
};
/** /**
* Initializes the toolbox. * Initializes the toolbox.
*/ */
Blockly.Toolbox.init = function() { Blockly.Toolbox.prototype.init = function() {
Blockly.Toolbox.CONFIG_['cleardotPath'] = this.CONFIG_['cleardotPath'] = Blockly.pathToMedia + '1x1.gif';
Blockly.pathToMedia + '1x1.gif'; this.CONFIG_['cssCollapsedFolderIcon'] =
Blockly.Toolbox.CONFIG_['cssCollapsedFolderIcon'] =
'blocklyTreeIconClosed' + (Blockly.RTL ? 'Rtl' : 'Ltr'); 'blocklyTreeIconClosed' + (Blockly.RTL ? 'Rtl' : 'Ltr');
var tree = new Blockly.Toolbox.TreeControl(goog.html.SafeHtml.EMPTY, var tree = new Blockly.Toolbox.TreeControl(this, this.CONFIG_);
Blockly.Toolbox.CONFIG_); this.tree_ = tree;
Blockly.Toolbox.tree_ = tree;
tree.setShowRootNode(false); tree.setShowRootNode(false);
tree.setShowLines(false); tree.setShowLines(false);
tree.setShowExpandIcons(false); tree.setShowExpandIcons(false);
tree.setSelectedItem(null); tree.setSelectedItem(null);
Blockly.Toolbox.HtmlDiv.style.display = 'block'; this.HtmlDiv.style.display = 'block';
Blockly.Toolbox.flyout_.init(Blockly.mainWorkspace); this.flyout_.init(Blockly.mainWorkspace);
Blockly.Toolbox.populate_(); this.populate_();
tree.render(Blockly.Toolbox.HtmlDiv); tree.render(this.HtmlDiv);
// If the document resizes, reposition the toolbox. // If the document resizes, reposition the toolbox.
var thisToolbox = this;
goog.events.listen(window, goog.events.EventType.RESIZE, goog.events.listen(window, goog.events.EventType.RESIZE,
Blockly.Toolbox.position_); function() {thisToolbox.position_();});
Blockly.Toolbox.position_(); this.position_();
}; };
/** /**
* Move the toolbox to the edge. * Move the toolbox to the edge.
* @private * @private
*/ */
Blockly.Toolbox.position_ = function() { Blockly.Toolbox.prototype.position_ = function() {
var treeDiv = Blockly.Toolbox.HtmlDiv; var treeDiv = this.HtmlDiv;
var svgBox = goog.style.getBorderBox(Blockly.svg); var svgBox = goog.style.getBorderBox(Blockly.svg);
var svgSize = Blockly.svgSize(); var svgSize = Blockly.svgSize();
if (Blockly.RTL) { if (Blockly.RTL) {
...@@ -139,10 +140,10 @@ Blockly.Toolbox.position_ = function() { ...@@ -139,10 +140,10 @@ Blockly.Toolbox.position_ = function() {
treeDiv.style.marginLeft = svgBox.left; treeDiv.style.marginLeft = svgBox.left;
} }
treeDiv.style.height = (svgSize.height + 1) + 'px'; treeDiv.style.height = (svgSize.height + 1) + 'px';
Blockly.Toolbox.width = treeDiv.offsetWidth; this.width = treeDiv.offsetWidth;
if (!Blockly.RTL) { if (!Blockly.RTL) {
// For some reason the LTR toolbox now reports as 1px too wide. // For some reason the LTR toolbox now reports as 1px too wide.
Blockly.Toolbox.width -= 1; this.width -= 1;
} }
}; };
...@@ -150,8 +151,8 @@ Blockly.Toolbox.position_ = function() { ...@@ -150,8 +151,8 @@ Blockly.Toolbox.position_ = function() {
* Fill the toolbox with categories and blocks. * Fill the toolbox with categories and blocks.
* @private * @private
*/ */
Blockly.Toolbox.populate_ = function() { Blockly.Toolbox.prototype.populate_ = function() {
var rootOut = Blockly.Toolbox.tree_; var rootOut = this.tree_;
rootOut.removeChildren(); // Delete any existing content. rootOut.removeChildren(); // Delete any existing content.
rootOut.blocks = []; rootOut.blocks = [];
function syncTrees(treeIn, treeOut) { function syncTrees(treeIn, treeOut) {
...@@ -179,7 +180,7 @@ Blockly.Toolbox.populate_ = function() { ...@@ -179,7 +180,7 @@ Blockly.Toolbox.populate_ = function() {
} }
} }
} }
syncTrees(Blockly.languageTree, Blockly.Toolbox.tree_); syncTrees(Blockly.languageTree, this.tree_);
if (rootOut.blocks.length) { if (rootOut.blocks.length) {
throw 'Toolbox cannot have both blocks and categories in the root level.'; throw 'Toolbox cannot have both blocks and categories in the root level.';
...@@ -192,24 +193,23 @@ Blockly.Toolbox.populate_ = function() { ...@@ -192,24 +193,23 @@ Blockly.Toolbox.populate_ = function() {
/** /**
* Unhighlight any previously specified option. * Unhighlight any previously specified option.
*/ */
Blockly.Toolbox.clearSelection = function() { Blockly.Toolbox.prototype.clearSelection = function() {
Blockly.Toolbox.tree_.setSelectedItem(null); this.tree_.setSelectedItem(null);
}; };
// Extending Closure's Tree UI. // Extending Closure's Tree UI.
/** /**
* Extention of a TreeControl object that uses a custom tree node. * Extention of a TreeControl object that uses a custom tree node.
* @param {!goog.html.SafeHtml} html The HTML content of the node label. * @param {Blockly.Toolbox} toolbox The parent toolbox for this tree.
* @param {Object=} opt_config The configuration for the tree. See * @param {Object} config The configuration for the tree. See
* goog.ui.tree.TreeControl.DefaultConfig. If not specified, a default config * goog.ui.tree.TreeControl.DefaultConfig.
* will be used.
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
* @constructor * @constructor
* @extends {goog.ui.tree.TreeControl} * @extends {goog.ui.tree.TreeControl}
*/ */
Blockly.Toolbox.TreeControl = function(html, opt_config, opt_domHelper) { Blockly.Toolbox.TreeControl = function(toolbox, config) {
goog.ui.tree.TreeControl.call(this, html, opt_config, opt_domHelper); this.toolbox_ = toolbox;
goog.ui.tree.TreeControl.call(this, goog.html.SafeHtml.EMPTY, config);
}; };
goog.inherits(Blockly.Toolbox.TreeControl, goog.ui.tree.TreeControl); goog.inherits(Blockly.Toolbox.TreeControl, goog.ui.tree.TreeControl);
...@@ -251,7 +251,7 @@ Blockly.Toolbox.TreeControl.prototype.handleTouchEvent_ = function(e) { ...@@ -251,7 +251,7 @@ Blockly.Toolbox.TreeControl.prototype.handleTouchEvent_ = function(e) {
* @override * @override
*/ */
Blockly.Toolbox.TreeControl.prototype.createNode = function(opt_html) { Blockly.Toolbox.TreeControl.prototype.createNode = function(opt_html) {
return new Blockly.Toolbox.TreeNode(opt_html ? return new Blockly.Toolbox.TreeNode(this.toolbox_, opt_html ?
goog.html.SafeHtml.htmlEscape(opt_html) : goog.html.SafeHtml.EMPTY, goog.html.SafeHtml.htmlEscape(opt_html) : goog.html.SafeHtml.EMPTY,
this.getConfig(), this.getDomHelper()); this.getConfig(), this.getDomHelper());
}; };
...@@ -267,15 +267,16 @@ Blockly.Toolbox.TreeControl.prototype.setSelectedItem = function(node) { ...@@ -267,15 +267,16 @@ Blockly.Toolbox.TreeControl.prototype.setSelectedItem = function(node) {
} }
goog.ui.tree.TreeControl.prototype.setSelectedItem.call(this, node); goog.ui.tree.TreeControl.prototype.setSelectedItem.call(this, node);
if (node && node.blocks && node.blocks.length) { if (node && node.blocks && node.blocks.length) {
Blockly.Toolbox.flyout_.show(node.blocks); this.toolbox_.flyout_.show(node.blocks);
} else { } else {
// Hide the flyout. // Hide the flyout.
Blockly.Toolbox.flyout_.hide(); this.toolbox_.flyout_.hide();
} }
}; };
/** /**
* A single node in the tree, customized for Blockly's UI. * A single node in the tree, customized for Blockly's UI.
* @param {Blockly.Toolbox} toolbox The parent toolbox for this tree.
* @param {!goog.html.SafeHtml} html The HTML content of the node label. * @param {!goog.html.SafeHtml} html The HTML content of the node label.
* @param {Object=} opt_config The configuration for the tree. See * @param {Object=} opt_config The configuration for the tree. See
* goog.ui.tree.TreeControl.DefaultConfig. If not specified, a default config * goog.ui.tree.TreeControl.DefaultConfig. If not specified, a default config
...@@ -284,16 +285,18 @@ Blockly.Toolbox.TreeControl.prototype.setSelectedItem = function(node) { ...@@ -284,16 +285,18 @@ Blockly.Toolbox.TreeControl.prototype.setSelectedItem = function(node) {
* @constructor * @constructor
* @extends {goog.ui.tree.TreeNode} * @extends {goog.ui.tree.TreeNode}
*/ */
Blockly.Toolbox.TreeNode = function(html, opt_config, opt_domHelper) { Blockly.Toolbox.TreeNode = function(toolbox, html, opt_config, opt_domHelper) {
goog.ui.tree.TreeNode.call(this, html, opt_config, opt_domHelper); goog.ui.tree.TreeNode.call(this, html, opt_config, opt_domHelper);
if (toolbox) {
var resize = function() { var resize = function() {
Blockly.fireUiEvent(window, 'resize'); Blockly.fireUiEvent(window, 'resize');
}; };
// Fire a resize event since the toolbox may have changed width. // Fire a resize event since the toolbox may have changed width.
goog.events.listen(Blockly.Toolbox.tree_, goog.events.listen(toolbox.tree_,
goog.ui.tree.BaseNode.EventType.EXPAND, resize); goog.ui.tree.BaseNode.EventType.EXPAND, resize);
goog.events.listen(Blockly.Toolbox.tree_, goog.events.listen(toolbox.tree_,
goog.ui.tree.BaseNode.EventType.COLLAPSE, resize); goog.ui.tree.BaseNode.EventType.COLLAPSE, resize);
}
}; };
goog.inherits(Blockly.Toolbox.TreeNode, goog.ui.tree.TreeNode); goog.inherits(Blockly.Toolbox.TreeNode, goog.ui.tree.TreeNode);
...@@ -337,10 +340,10 @@ Blockly.Toolbox.TreeNode.prototype.onDoubleClick_ = function(e) { ...@@ -337,10 +340,10 @@ Blockly.Toolbox.TreeNode.prototype.onDoubleClick_ = function(e) {
/** /**
* A blank separator node in the tree. * A blank separator node in the tree.
* @constructor * @constructor
* @extends {Blockly.Toolbox.TreeNode} * @extends {Blockly.Toolbox.prototype.TreeNode}
*/ */
Blockly.Toolbox.TreeSeparator = function() { Blockly.Toolbox.TreeSeparator = function() {
Blockly.Toolbox.TreeNode.call(this, '', Blockly.Toolbox.TreeNode.call(this, null, '',
Blockly.Toolbox.TreeSeparator.CONFIG_); Blockly.Toolbox.TreeSeparator.CONFIG_);
}; };
goog.inherits(Blockly.Toolbox.TreeSeparator, Blockly.Toolbox.TreeNode); goog.inherits(Blockly.Toolbox.TreeSeparator, Blockly.Toolbox.TreeNode);
......
...@@ -30,7 +30,6 @@ goog.provide('Blockly.Variables'); ...@@ -30,7 +30,6 @@ goog.provide('Blockly.Variables');
// TODO(scr): Fix circular dependencies // TODO(scr): Fix circular dependencies
// goog.require('Blockly.Block'); // goog.require('Blockly.Block');
goog.require('Blockly.Toolbox');
goog.require('Blockly.Workspace'); goog.require('Blockly.Workspace');
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment