Adds Ability to Add a Flyout as a Plugin (#4059)

This commit is contained in:
alschmiedt
2020-07-22 17:13:19 -07:00
committed by GitHub
parent 26993a120f
commit 0f00f42580
13 changed files with 238 additions and 30 deletions

View File

@@ -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'], [], {});

View File

@@ -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);

View File

@@ -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
*/

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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();
}
}
}

173
core/interfaces/i_flyout.js Normal file
View File

@@ -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<!SVGSVGElement>|
* !Blockly.utils.dom.SvgElementType<!SVGGElement>} tagName The type of tag to
* put the flyout in. This should be <svg> or <g>.
* @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;

View File

@@ -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;

View File

@@ -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.IFlyout>} */
Blockly.registry.Type.FLYOUTS_VERTICAL_TOOLBOX =
new Blockly.registry.Type('flyouts-vertical-toolbox');
/** @type {!Blockly.registry.Type<Blockly.IFlyout>} */
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<T>} type The type of the plugin.

View File

@@ -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_;

View File

@@ -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;

View File

@@ -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