diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index 227e51bbc..47f400da5 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -64,8 +64,8 @@ goog.addDependency('../../core/field_variable.js', ['Blockly.FieldVariable'], [' goog.addDependency('../../core/flyout_base.js', ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.FlyoutCursor', 'Blockly.Gesture', 'Blockly.Marker', 'Blockly.Scrollbar', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {}); goog.addDependency('../../core/flyout_button.js', ['Blockly.FlyoutButton'], ['Blockly.Css', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {'lang': 'es5'}); goog.addDependency('../../core/flyout_dragger.js', ['Blockly.FlyoutDragger'], ['Blockly.WorkspaceDragger', 'Blockly.utils.object'], {}); -goog.addDependency('../../core/flyout_horizontal.js', ['Blockly.HorizontalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object'], {}); -goog.addDependency('../../core/flyout_vertical.js', ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/flyout_horizontal.js', ['Blockly.HorizontalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/flyout_vertical.js', ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {}); goog.addDependency('../../core/generator.js', ['Blockly.Generator'], ['Blockly.Block'], {}); goog.addDependency('../../core/gesture.js', ['Blockly.Gesture'], ['Blockly.ASTNode', 'Blockly.BlockDragger', 'Blockly.BubbleDragger', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.FlyoutDragger', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.WorkspaceDragger', 'Blockly.blockAnimations', 'Blockly.constants', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate'], {}); goog.addDependency('../../core/grid.js', ['Blockly.Grid'], ['Blockly.utils.dom', 'Blockly.utils.userAgent'], {}); @@ -79,6 +79,7 @@ goog.addDependency('../../core/interfaces/i_connection_checker.js', ['Blockly.IC goog.addDependency('../../core/interfaces/i_copyable.js', ['Blockly.ICopyable'], [], {}); goog.addDependency('../../core/interfaces/i_deletable.js', ['Blockly.IDeletable'], [], {}); goog.addDependency('../../core/interfaces/i_deletearea.js', ['Blockly.IDeleteArea'], [], {}); +goog.addDependency('../../core/interfaces/i_flyout.js', ['Blockly.IFlyout'], [], {}); goog.addDependency('../../core/interfaces/i_movable.js', ['Blockly.IMovable'], [], {}); goog.addDependency('../../core/interfaces/i_registrable.js', ['Blockly.IRegistrable'], [], {}); goog.addDependency('../../core/interfaces/i_selectable.js', ['Blockly.ISelectable'], [], {}); diff --git a/core/flyout_base.js b/core/flyout_base.js index c51777d57..0d0720683 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -31,6 +31,7 @@ goog.require('Blockly.Xml'); goog.requireType('Blockly.IBlocklyActionable'); goog.requireType('Blockly.IDeleteArea'); +goog.requireType('Blockly.IFlyout'); goog.requireType('Blockly.utils.Metrics'); @@ -42,6 +43,7 @@ goog.requireType('Blockly.utils.Metrics'); * @abstract * @implements {Blockly.IBlocklyActionable} * @implements {Blockly.IDeleteArea} + * @implements {Blockly.IFlyout} */ Blockly.Flyout = function(workspaceOptions) { workspaceOptions.getMetrics = @@ -481,7 +483,6 @@ Blockly.Flyout.prototype.show = function(flyoutDef) { } } this.setVisible(true); - // Parse the Array or NodeList passed in into an Array of // Blockly.utils.toolbox.Toolbox. var parsedContent = Blockly.utils.toolbox.convertToolboxToJSON(flyoutDef); diff --git a/core/flyout_dragger.js b/core/flyout_dragger.js index f49243b7f..0a9bafe67 100644 --- a/core/flyout_dragger.js +++ b/core/flyout_dragger.js @@ -15,6 +15,8 @@ goog.provide('Blockly.FlyoutDragger'); goog.require('Blockly.utils.object'); goog.require('Blockly.WorkspaceDragger'); +goog.requireType('Blockly.IFlyout'); + /** * Class for a flyout dragger. It moves a flyout workspace around when it is @@ -22,7 +24,7 @@ goog.require('Blockly.WorkspaceDragger'); * Note that the workspace itself manages whether or not it has a drag surface * and how to do translations based on that. This simply passes the right * commands based on events. - * @param {!Blockly.Flyout} flyout The flyout to drag. + * @param {!Blockly.IFlyout} flyout The flyout to drag. * @extends {Blockly.WorkspaceDragger} * @constructor */ diff --git a/core/flyout_horizontal.js b/core/flyout_horizontal.js index 7f648ec65..d365438ab 100644 --- a/core/flyout_horizontal.js +++ b/core/flyout_horizontal.js @@ -14,6 +14,7 @@ goog.provide('Blockly.HorizontalFlyout'); goog.require('Blockly.Block'); goog.require('Blockly.Flyout'); +goog.require('Blockly.registry'); goog.require('Blockly.Scrollbar'); goog.require('Blockly.utils'); goog.require('Blockly.utils.object'); @@ -32,7 +33,6 @@ goog.requireType('Blockly.utils.Metrics'); */ Blockly.HorizontalFlyout = function(workspaceOptions) { Blockly.HorizontalFlyout.superClass_.constructor.call(this, workspaceOptions); - this.horizontalLayout = true; }; Blockly.utils.object.inherits(Blockly.HorizontalFlyout, Blockly.Flyout); @@ -83,12 +83,12 @@ Blockly.HorizontalFlyout.prototype.getMetrics_ = function() { contentWidth: (optionBox.width + 2 * this.MARGIN) * this.workspace_.scale, contentTop: 0, contentLeft: 0, - + viewHeight: viewHeight, viewWidth: viewWidth, viewTop: -this.workspace_.scrollY, viewLeft: -this.workspace_.scrollX, - + absoluteTop: absoluteTop, absoluteLeft: absoluteLeft }; @@ -372,3 +372,6 @@ Blockly.HorizontalFlyout.prototype.reflowInternal_ = function() { this.position(); } }; + +Blockly.registry.register(Blockly.registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX, + Blockly.registry.DEFAULT, Blockly.HorizontalFlyout); diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js index 139277cc2..c0976422e 100644 --- a/core/flyout_vertical.js +++ b/core/flyout_vertical.js @@ -14,6 +14,7 @@ goog.provide('Blockly.VerticalFlyout'); goog.require('Blockly.Block'); goog.require('Blockly.Flyout'); +goog.require('Blockly.registry'); goog.require('Blockly.Scrollbar'); goog.require('Blockly.utils'); goog.require('Blockly.utils.object'); @@ -36,6 +37,12 @@ Blockly.VerticalFlyout = function(workspaceOptions) { }; Blockly.utils.object.inherits(Blockly.VerticalFlyout, Blockly.Flyout); +/** + * The name of the vertical flyout in the registry. + * @type {string} + */ +Blockly.VerticalFlyout.registryName = 'verticalFlyout'; + /** * Return an object with all the metrics required to size scrollbars for the * flyout. The following properties are computed: @@ -374,3 +381,6 @@ Blockly.VerticalFlyout.prototype.reflowInternal_ = function() { this.position(); } }; + +Blockly.registry.register(Blockly.registry.Type.FLYOUTS_VERTICAL_TOOLBOX, + Blockly.registry.DEFAULT, Blockly.VerticalFlyout); diff --git a/core/gesture.js b/core/gesture.js index cf5d94ef5..3d5c933fe 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -28,6 +28,8 @@ goog.require('Blockly.utils'); goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.WorkspaceDragger'); +goog.requireType('Blockly.IFlyout'); + /** * Note: In this file "start" refers to touchstart, mousedown, and pointerstart @@ -190,7 +192,7 @@ Blockly.Gesture = function(e, creatorWorkspace) { /** * The flyout a gesture started in, if any. - * @type {Blockly.Flyout} + * @type {Blockly.IFlyout} * @private */ this.flyout_ = null; @@ -670,7 +672,7 @@ Blockly.Gesture.prototype.fireWorkspaceClick_ = function(ws) { /** * Handle a mousedown/touchstart event on a flyout. * @param {!Event} e A mouse down or touch start event. - * @param {!Blockly.Flyout} flyout The flyout the event hit. + * @param {!Blockly.IFlyout} flyout The flyout the event hit. * @package */ Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) { @@ -867,7 +869,7 @@ Blockly.Gesture.prototype.setStartWorkspace_ = function(ws) { /** * Record the flyout that a gesture started on. - * @param {Blockly.Flyout} flyout The flyout the gesture started on. + * @param {Blockly.IFlyout} flyout The flyout the gesture started on. * @private */ Blockly.Gesture.prototype.setStartFlyout_ = function(flyout) { diff --git a/core/inject.js b/core/inject.js index d325ea617..d299d9997 100644 --- a/core/inject.js +++ b/core/inject.js @@ -369,7 +369,9 @@ Blockly.init_ = function(mainWorkspace) { // Build a fixed flyout with the root blocks. flyout.init(mainWorkspace); flyout.show(options.languageTree); - flyout.scrollToStart(); + if (typeof flyout.scrollToStart == 'function') { + flyout.scrollToStart(); + } } } diff --git a/core/interfaces/i_flyout.js b/core/interfaces/i_flyout.js new file mode 100644 index 000000000..abd2c591e --- /dev/null +++ b/core/interfaces/i_flyout.js @@ -0,0 +1,173 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview The interface for a flyout. + * @author aschmiedt@google.com (Abby Schmiedt) + */ + +'use strict'; + +goog.provide('Blockly.IFlyout'); + +goog.requireType('Blockly.IRegistrable'); + + +/** + * Interface for a flyout. + * @extends {Blockly.IRegistrable} + * @interface + */ +Blockly.IFlyout = function() {}; + +/** + * Whether the flyout is laid out horizontally or not. + * @type {boolean} + */ +Blockly.IFlyout.prototype.horizontalLayout; + +/** + * Is RTL vs LTR. + * @type {boolean} + */ +Blockly.IFlyout.prototype.RTL; + +/** + * The target workspace + * @type {?Blockly.WorkspaceSvg} + */ +Blockly.IFlyout.prototype.targetWorkspace; + +/** + * Margin around the edges of the blocks in the flyout. + * @type {number} + * @const + */ +Blockly.IFlyout.prototype.MARGIN; + +/** + * Does the flyout automatically close when a block is created? + * @type {boolean} + */ +Blockly.IFlyout.prototype.autoClose; + +/** + * Corner radius of the flyout background. + * @type {number} + * @const + */ +Blockly.IFlyout.prototype.CORNER_RADIUS; + +/** + * Creates the flyout's DOM. Only needs to be called once. The flyout can + * either exist as its own svg element or be a g element nested inside a + * separate svg element. + * @param {string| + * !Blockly.utils.dom.SvgElementType| + * !Blockly.utils.dom.SvgElementType} tagName The type of tag to + * put the flyout in. This should be or . + * @return {!SVGElement} The flyout's SVG group. + */ +Blockly.IFlyout.prototype.createDom; + +/** + * Initializes the flyout. + * @param {!Blockly.WorkspaceSvg} targetWorkspace The workspace in which to + * create new blocks. + */ +Blockly.IFlyout.prototype.init; + +/** + * Dispose of this flyout. + * Unlink from all DOM elements to prevent memory leaks. + */ +Blockly.IFlyout.prototype.dispose; + +/** + * Get the width of the flyout. + * @return {number} The width of the flyout. + */ +Blockly.IFlyout.prototype.getWidth; + +/** + * Get the height of the flyout. + * @return {number} The width of the flyout. + */ +Blockly.IFlyout.prototype.getHeight; + +/** + * Get the workspace inside the flyout. + * @return {!Blockly.WorkspaceSvg} The workspace inside the flyout. + */ +Blockly.IFlyout.prototype.getWorkspace; + +/** + * Is the flyout visible? + * @return {boolean} True if visible. + */ +Blockly.IFlyout.prototype.isVisible; + +/** + * Set whether the flyout is visible. A value of true does not necessarily mean + * that the flyout is shown. It could be hidden because its container is hidden. + * @param {boolean} visible True if visible. + */ +Blockly.IFlyout.prototype.setVisible; + +/** + * Set whether this flyout's container is visible. + * @param {boolean} visible Whether the container is visible. + */ +Blockly.IFlyout.prototype.setContainerVisible; + +/** + * Hide and empty the flyout. + */ +Blockly.IFlyout.prototype.hide; + +/** + * Show and populate the flyout. + * @param {!Blockly.utils.toolbox.ToolboxDefinition|string} flyoutDef + * List of contents to display in the flyout as an array of xml an + * array of Nodes, a NodeList or a string with the name of the dynamic category. + * Variables and procedures have a custom set of blocks. + */ +Blockly.IFlyout.prototype.show; + +/** + * Create a copy of this block on the workspace. + * @param {!Blockly.BlockSvg} originalBlock The block to copy from the flyout. + * @return {!Blockly.BlockSvg} The newly created block. + * @throws {Error} if something went wrong with deserialization. + */ +Blockly.IFlyout.prototype.createBlock; + +/** + * Reflow blocks and their mats. + */ +Blockly.IFlyout.prototype.reflow; + +/** + * @return {boolean} True if this flyout may be scrolled with a scrollbar or by + * dragging. + */ +Blockly.IFlyout.prototype.isScrollable; + +/** + * Position the flyout. + * @return {void} + */ +Blockly.IFlyout.prototype.position; + +/** + * Determine if a drag delta is toward the workspace, based on the position + * and orientation of the flyout. This is used in determineDragIntention_ to + * determine if a new block should be created or if the flyout should scroll. + * @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has + * moved from the position at mouse down, in pixel units. + * @return {boolean} True if the drag is toward the workspace. + */ +Blockly.IFlyout.prototype.isDragTowardWorkspace; diff --git a/core/interfaces/i_toolbox.js b/core/interfaces/i_toolbox.js index 456829fc5..62ac90ce0 100644 --- a/core/interfaces/i_toolbox.js +++ b/core/interfaces/i_toolbox.js @@ -13,6 +13,7 @@ goog.provide('Blockly.IToolbox'); +goog.requireType('Blockly.IFlyout'); goog.requireType('Blockly.IRegistrable'); @@ -56,7 +57,7 @@ Blockly.IToolbox.prototype.getHeight; /** * Get the toolbox flyout. - * @return {Blockly.Flyout} The toolbox flyout. + * @return {Blockly.IFlyout} The toolbox flyout. */ Blockly.IToolbox.prototype.getFlyout; diff --git a/core/registry.js b/core/registry.js index ffa5c6c14..b0e808ac4 100644 --- a/core/registry.js +++ b/core/registry.js @@ -16,6 +16,7 @@ goog.provide('Blockly.registry'); goog.requireType('Blockly.blockRendering.Renderer'); goog.requireType('Blockly.Field'); goog.requireType('Blockly.IConnectionChecker'); +goog.requireType('Blockly.IFlyout'); goog.requireType('Blockly.IToolbox'); goog.requireType('Blockly.Theme'); goog.requireType('Blockly.utils.toolbox'); @@ -75,6 +76,14 @@ Blockly.registry.Type.THEME = new Blockly.registry.Type('theme'); Blockly.registry.Type.CONNECTION_CHECKER = new Blockly.registry.Type('connectionChecker'); +/** @type {!Blockly.registry.Type} */ +Blockly.registry.Type.FLYOUTS_VERTICAL_TOOLBOX = + new Blockly.registry.Type('flyouts-vertical-toolbox'); + +/** @type {!Blockly.registry.Type} */ +Blockly.registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX = + new Blockly.registry.Type('flyouts-horizontal-toolbox'); + /** * Registers a class based on a type and name. * @param {string|Blockly.registry.Type} type The type of the plugin. diff --git a/core/toolbox.js b/core/toolbox.js index 080653d2d..30ffa319d 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -30,6 +30,7 @@ goog.require('Blockly.utils.toolbox'); goog.requireType('Blockly.IBlocklyActionable'); goog.requireType('Blockly.IDeleteArea'); +goog.requireType('Blockly.IFlyout'); goog.requireType('Blockly.IStyleable'); goog.requireType('Blockly.IToolbox'); @@ -113,7 +114,7 @@ Blockly.Toolbox = function(workspace) { /** * The toolbox flyout. - * @type {Blockly.Flyout} + * @type {Blockly.IFlyout} * @private */ this.flyout_ = null; @@ -184,22 +185,22 @@ Blockly.Toolbox.prototype.init = function() { })); workspaceOptions.toolboxPosition = workspace.options.toolboxPosition; + var FlyoutClass = null; if (workspace.horizontalLayout) { - if (!Blockly.HorizontalFlyout) { - throw Error('Missing require for Blockly.HorizontalFlyout'); - } - this.flyout_ = new Blockly.HorizontalFlyout(workspaceOptions); + FlyoutClass = Blockly.registry.getClassFromOptions( + Blockly.registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX, workspace.options); } else { - if (!Blockly.VerticalFlyout) { - throw Error('Missing require for Blockly.VerticalFlyout'); - } - this.flyout_ = new Blockly.VerticalFlyout(workspaceOptions); + FlyoutClass = Blockly.registry.getClassFromOptions( + Blockly.registry.Type.FLYOUTS_VERTICAL_TOOLBOX, workspace.options); } - if (!this.flyout_) { - throw Error('One of Blockly.VerticalFlyout or Blockly.Horizontal must be' + - 'required.'); + + if (!FlyoutClass) { + throw Error('Blockly.VerticalFlyout, Blockly.HorizontalFlyout or your own' + + ' custom flyout must be required.'); } + this.flyout_ = new FlyoutClass(workspaceOptions); + // Insert the flyout after the workspace. Blockly.utils.dom.insertAfter( this.flyout_.createDom(Blockly.utils.dom.SvgElementType.SVG), svg); @@ -440,7 +441,8 @@ Blockly.Toolbox.prototype.handleAfterTreeSelected_ = function( if (newNode && newNode.contents && newNode.contents.length) { this.flyout_.show(newNode.contents); // Scroll the flyout to the top if the category has changed. - if (this.lastCategory_ != newNode) { + if (this.lastCategory_ != newNode && + typeof this.flyout_.scrollToStart == 'function') { this.flyout_.scrollToStart(); } if (this.workspace_.keyboardAccessibilityMode) { @@ -538,7 +540,7 @@ Blockly.Toolbox.prototype.getHeight = function() { /** * Get the toolbox flyout. - * @return {Blockly.Flyout} The toolbox flyout. + * @return {Blockly.IFlyout} The toolbox flyout. */ Blockly.Toolbox.prototype.getFlyout = function() { return this.flyout_; diff --git a/core/trashcan.js b/core/trashcan.js index 75f0a9478..9c0168eae 100644 --- a/core/trashcan.js +++ b/core/trashcan.js @@ -18,6 +18,7 @@ goog.require('Blockly.utils.Rect'); goog.require('Blockly.Xml'); goog.requireType('Blockly.IDeleteArea'); +goog.requireType('Blockly.IFlyout'); /** @@ -43,7 +44,7 @@ Blockly.Trashcan = function(workspace) { /** * The trashcan flyout. - * @type {Blockly.Flyout} + * @type {Blockly.IFlyout} * @package */ this.flyout = null; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 97739c44d..e6d21cb3e 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -44,6 +44,7 @@ goog.require('Blockly.Xml'); goog.requireType('Blockly.blockRendering.Renderer'); goog.requireType('Blockly.IASTNodeLocationSvg'); goog.requireType('Blockly.IBoundedElement'); +goog.requireType('Blockly.IFlyout'); /** @@ -336,7 +337,7 @@ Blockly.WorkspaceSvg.prototype.scrollbar = null; /** * Fixed flyout providing blocks which may be dragged into this workspace. - * @type {Blockly.Flyout} + * @type {Blockly.IFlyout} * @private */ Blockly.WorkspaceSvg.prototype.flyout_ = null; @@ -946,7 +947,7 @@ Blockly.WorkspaceSvg.prototype.addFlyout = function(tagName) { * owned by either the toolbox or the workspace, depending on toolbox * configuration. It will be null if there is no flyout. * @param {boolean=} opt_own Only return the workspace's own flyout if True. - * @return {Blockly.Flyout} The flyout on this workspace. + * @return {Blockly.IFlyout} The flyout on this workspace. * @package */ Blockly.WorkspaceSvg.prototype.getFlyout = function(opt_own) { @@ -2107,7 +2108,7 @@ Blockly.WorkspaceSvg.prototype.scroll = function(x, y) { /** * Get the dimensions of the given workspace component, in pixels. - * @param {Blockly.IToolbox|Blockly.Flyout} elem The element to get the + * @param {Blockly.IToolbox|Blockly.IFlyout} elem The element to get the * dimensions of, or null. It should be a toolbox or flyout, and should * implement getWidth() and getHeight(). * @return {!Blockly.utils.Size} An object containing width and height