Toolbox interface (#3918)

* Add toolbox interface
This commit is contained in:
alschmiedt
2020-05-27 10:06:18 -07:00
committed by GitHub
parent 2450a74101
commit 6bc60894db
11 changed files with 192 additions and 12 deletions

View File

@@ -27,6 +27,7 @@ goog.requireType('Blockly.blockRendering.ConstantProvider');
goog.requireType('Blockly.IASTNodeLocationSvg');
goog.requireType('Blockly.IASTNodeLocationWithBlock');
goog.requireType('Blockly.IBlocklyActionable');
goog.requireType('Blockly.IRegistrable');
/**
@@ -42,6 +43,7 @@ goog.requireType('Blockly.IBlocklyActionable');
* @implements {Blockly.IASTNodeLocationSvg}
* @implements {Blockly.IASTNodeLocationWithBlock}
* @implements {Blockly.IBlocklyActionable}
* @implements {Blockly.IRegistrable}
*/
Blockly.Field = function(value, opt_validator, opt_config) {
/**

View File

@@ -30,6 +30,7 @@ goog.require('Blockly.WorkspaceSvg');
goog.require('Blockly.Xml');
goog.requireType('Blockly.IBlocklyActionable');
goog.requireType('Blockly.IDeleteArea');
goog.requireType('Blockly.utils.Metrics');
@@ -40,6 +41,7 @@ goog.requireType('Blockly.utils.Metrics');
* @constructor
* @abstract
* @implements {Blockly.IBlocklyActionable}
* @implements {Blockly.IDeleteArea}
*/
Blockly.Flyout = function(workspaceOptions) {
workspaceOptions.getMetrics =

View File

@@ -0,0 +1,28 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The interface for a component that can delete a block that is
* dropped on top of it.
* @author aschmiedt@google.com (Abby Schmiedt)
*/
'use strict';
goog.provide('Blockly.IDeleteArea');
/**
* Interface for a component that can delete a block that is dropped on top of it.
* @interface
*/
Blockly.IDeleteArea = function() {};
/**
* Return the deletion rectangle.
* @return {Blockly.utils.Rect} Rectangle in which to delete.
*/
Blockly.IDeleteArea.prototype.getClientRect;

View File

@@ -0,0 +1,19 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The interface for a Blockly component that can be registered.
* (Ex. Toolbox, Fields, Renderers)
* @author aschmiedt@google.com (Abby Schmiedt)
*/
'use strict';
goog.provide('Blockly.IRegistrable');
/** @interface */
Blockly.IRegistrable = function() {};

View File

@@ -0,0 +1,111 @@
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The interface for a toolbox.
* @author aschmiedt@google.com (Abby Schmiedt)
*/
'use strict';
goog.provide('Blockly.IToolbox');
goog.requireType('Blockly.IRegistrable');
/**
* Interface for a toolbox.
* @extends {Blockly.IRegistrable}
* @interface
*/
Blockly.IToolbox = function() {};
/**
* Initializes the toolbox.
* @return {void}
*/
Blockly.IToolbox.prototype.init;
/**
* Fill the toolbox with categories and blocks.
* @param {Array.<Blockly.utils.toolbox.Toolbox>} toolboxDef Array holding objects
* containing information on the contents of the toolbox.
*/
Blockly.IToolbox.prototype.render;
/**
* Dispose of this toolbox.
* @return {void}
*/
Blockly.IToolbox.prototype.dispose;
/**
* Get the width of the toolbox.
* @return {number} The width of the toolbox.
*/
Blockly.IToolbox.prototype.getWidth;
/**
* Get the height of the toolbox.
* @return {number} The width of the toolbox.
*/
Blockly.IToolbox.prototype.getHeight;
/**
* Get the toolbox flyout.
* @return {Blockly.Flyout} The toolbox flyout.
*/
Blockly.IToolbox.prototype.getFlyout;
/**
* Move the toolbox to the edge.
* @return {void}
*/
Blockly.IToolbox.prototype.position;
/**
* Unhighlight any previously specified option.
* @return {void}
*/
Blockly.IToolbox.prototype.clearSelection;
/**
* Updates the category colours and background colour of selected categories.
* @return {void}
*/
Blockly.IToolbox.prototype.updateColourFromTheme;
/**
* Adds a style on the toolbox. Usually used to change the cursor.
* @param {string} style The name of the class to add.
*/
Blockly.IToolbox.prototype.addStyle;
/**
* Removes a style from the toolbox. Usually used to change the cursor.
* @param {string} style The name of the class to remove.
*/
Blockly.IToolbox.prototype.removeStyle;
/**
* Update the flyout's contents without closing it. Should be used in response
* to a change in one of the dynamic categories, such as variables or
* procedures.
* @return {void}
*/
Blockly.IToolbox.prototype.refreshSelection;
/**
* Toggles the visibility of the toolbox.
* @param {boolean} isVisible True if the toolbox should be visible.
*/
Blockly.IToolbox.prototype.setVisible;
/**
* Select the first toolbox category if no category is selected.
* @return {void}
*/
Blockly.IToolbox.prototype.selectFirstCategory;

View File

@@ -855,7 +855,8 @@ Blockly.navigation.flyoutOnAction_ = function(action) {
Blockly.navigation.toolboxOnAction_ = function(action) {
var workspace = Blockly.navigation.getNavigationWorkspace();
var toolbox = workspace.getToolbox();
var handled = toolbox ? toolbox.onBlocklyAction(action) : false;
var handled = toolbox && typeof toolbox.onBlocklyAction == 'function' ?
toolbox.onBlocklyAction(action) : false;
if (handled) {
return true;

View File

@@ -21,6 +21,7 @@ goog.require('Blockly.blockRendering.RenderInfo');
goog.require('Blockly.InsertionMarkerManager');
goog.requireType('Blockly.blockRendering.Debug');
goog.requireType('Blockly.IRegistrable');
/**
@@ -28,6 +29,7 @@ goog.requireType('Blockly.blockRendering.Debug');
* @param {string} name The renderer name.
* @package
* @constructor
* @implements {Blockly.IRegistrable}
*/
Blockly.blockRendering.Renderer = function(name) {

View File

@@ -28,6 +28,8 @@ goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.toolbox');
goog.requireType('Blockly.IBlocklyActionable');
goog.requireType('Blockly.IDeleteArea');
goog.requireType('Blockly.IToolbox');
/**
@@ -37,6 +39,8 @@ goog.requireType('Blockly.IBlocklyActionable');
* blocks.
* @constructor
* @implements {Blockly.IBlocklyActionable}
* @implements {Blockly.IDeleteArea}
* @implements {Blockly.IToolbox}
*/
Blockly.Toolbox = function(workspace) {
/**
@@ -206,7 +210,7 @@ Blockly.Toolbox.prototype.init = function() {
this.config_['cssCollapsedFolderIcon'] =
'blocklyTreeIconClosed' + (workspace.RTL ? 'Rtl' : 'Ltr');
this.renderTree(workspace.options.languageTree);
this.render(workspace.options.languageTree);
};
/**
@@ -215,7 +219,7 @@ Blockly.Toolbox.prototype.init = function() {
* containing information on the contents of the toolbox.
* @package
*/
Blockly.Toolbox.prototype.renderTree = function(toolboxDef) {
Blockly.Toolbox.prototype.render = function(toolboxDef) {
if (this.tree_) {
this.tree_.dispose(); // Delete any existing content.
this.lastCategory_ = null;
@@ -506,6 +510,14 @@ Blockly.Toolbox.prototype.dispose = function() {
this.lastCategory_ = null;
};
/**
* Toggles the visibility of the toolbox.
* @param {boolean} isVisible True if toolbox should be visible.
*/
Blockly.Toolbox.prototype.setVisible = function(isVisible) {
this.HtmlDiv.style.display = isVisible ? 'block' : 'none';
};
/**
* Get the width of the toolbox.
* @return {number} The width of the toolbox.

View File

@@ -17,11 +17,14 @@ goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.Xml');
goog.requireType('Blockly.IDeleteArea');
/**
* Class for a trash can.
* @param {!Blockly.WorkspaceSvg} workspace The workspace to sit in.
* @constructor
* @implements {Blockly.IDeleteArea}
*/
Blockly.Trashcan = function(workspace) {
/**

View File

@@ -1187,7 +1187,7 @@ Blockly.WorkspaceSvg.prototype.setVisible = function(isVisible) {
this.getParentSvg().style.display = isVisible ? 'block' : 'none';
if (this.toolbox_) {
// Currently does not support toolboxes in mutators.
this.toolbox_.HtmlDiv.style.display = isVisible ? 'block' : 'none';
this.toolbox_.setVisible(isVisible);
}
if (isVisible) {
var blocks = this.getAllBlocks(false);
@@ -1455,7 +1455,7 @@ Blockly.WorkspaceSvg.prototype.recordDeleteAreas = function() {
}
if (this.flyout_) {
this.deleteAreaToolbox_ = this.flyout_.getClientRect();
} else if (this.toolbox_) {
} else if (this.toolbox_ && typeof this.toolbox_.getClientRect == 'function') {
this.deleteAreaToolbox_ = this.toolbox_.getClientRect();
} else {
this.deleteAreaToolbox_ = null;
@@ -1849,7 +1849,7 @@ Blockly.WorkspaceSvg.prototype.updateToolbox = function(toolboxDef) {
throw Error('Existing toolbox has no categories. Can\'t change mode.');
}
this.options.languageTree = toolboxDef;
this.toolbox_.renderTree(toolboxDef);
this.toolbox_.render(toolboxDef);
} else {
if (!this.flyout_) {
throw Error('Existing toolbox has categories. Can\'t change mode.');

View File

@@ -53,7 +53,7 @@ suite('Toolbox', function() {
});
});
suite('renderTree', function() {
suite('render', function() {
setup(function() {
this.toolboxXml = Blockly.utils.toolbox.convertToolboxToJSON(this.toolboxXml);
this.toolbox.selectFirstCategory();
@@ -62,7 +62,7 @@ suite('Toolbox', function() {
this.toolbox.handleBeforeTreeSelected_(this.secondChild);
});
test('Tree is created and set', function() {
this.toolbox.renderTree(this.toolboxXml);
this.toolbox.render(this.toolboxXml);
chai.assert.isDefined(this.toolbox.tree_);
});
test('Throws error if a toolbox has both blocks and categories at root level', function() {
@@ -94,17 +94,17 @@ suite('Toolbox', function() {
}
];
chai.assert.throws(function() {
toolbox.renderTree(badToolboxDef);
toolbox.render(badToolboxDef);
}, 'Toolbox cannot have both blocks and categories in the root level.');
});
test('Select any open nodes', function() {
this.toolbox.renderTree(this.toolboxXml);
this.toolbox.render(this.toolboxXml);
var selectedNode = this.toolbox.tree_.children_[0];
chai.assert.isTrue(selectedNode.selected_);
});
test('Set the state for horizontal layout ', function() {
this.toolbox.horizontalLayout_ = true;
this.toolbox.renderTree(this.toolboxXml);
this.toolbox.render(this.toolboxXml);
var orientationAttribute = this.toolbox.tree_.getElement()
.getAttribute('aria-orientation');
chai.assert.equal(orientationAttribute, 'horizontal');
@@ -136,7 +136,7 @@ suite('Toolbox', function() {
]
}
];
this.toolbox.renderTree(jsonDef);
this.toolbox.render(jsonDef);
chai.assert.lengthOf(this.toolbox.tree_.children_, 1);
});
});