Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Movement Updates #2247

Merged
merged 43 commits into from
Feb 18, 2019
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
fb0b710
Added functionality to scrolling, dragging, and zooming.
BeksOmega Jan 15, 2019
9819aa6
Fixed incorrect changes to workspaceChanged function.
BeksOmega Feb 1, 2019
8c6f895
Fixed comment.
BeksOmega Feb 1, 2019
3fe39be
Fixed typo.
BeksOmega Feb 1, 2019
5fd9fa9
Removed scrollbar.set calls from workspace_svg.
BeksOmega Feb 2, 2019
42880fd
Removed scrollbar.resize() call.
BeksOmega Feb 2, 2019
c95f6de
Added move options to playground.
BeksOmega Feb 2, 2019
3242b7a
Fixed scroll_ calls that replaced scrollbar.set calls.
BeksOmega Feb 2, 2019
df44100
Removed this.scrollbar checks.
BeksOmega Feb 2, 2019
0dc9ac7
Changed zoom so that it always zooms towards the coordinates. Changed…
BeksOmega Feb 3, 2019
fa5563d
Changed procedures so the Highlight definition option is only availab…
BeksOmega Feb 3, 2019
dbeb7e7
Fixed scrollCenter so that it works with flyout toolboxes.
BeksOmega Feb 4, 2019
66ce3fd
Fixed zoomToFit so that it works with horizontal flyout toolboxes.
BeksOmega Feb 4, 2019
f2eb3ac
Fixed Typo.
BeksOmega Feb 4, 2019
4e839d5
Fixed bumping blocks when the workspace is not movable.
BeksOmega Feb 4, 2019
d81c4f4
Fixed bumping not working with left and top toolbox positions.
BeksOmega Feb 5, 2019
580361f
Re-Added not allowing scrollCenter if the workspace is not movable. D…
BeksOmega Feb 5, 2019
c3866c9
Cleaned up formatting.
BeksOmega Feb 5, 2019
6763fdc
Fixed bumping... again. Reformatted workspaceChanged a bit.
BeksOmega Feb 5, 2019
3e2da93
Changed blocks to be completely bumped into the workspace.
BeksOmega Feb 5, 2019
1ce779c
Reorganized metrics-getting for workspaceChanged.
BeksOmega Feb 5, 2019
89698ee
Added bumping workspace comments. Moved event checking.
BeksOmega Feb 5, 2019
55c30a7
Renamed workspaceChanged to bumpObjects.
BeksOmega Feb 5, 2019
2c74e2e
Added a bumpObjects developer reminder.
BeksOmega Feb 6, 2019
7036dd2
Added warning to zoomToFit.
BeksOmega Feb 6, 2019
549644f
Cleaned up some text.
BeksOmega Feb 6, 2019
0e4bf72
Added better inline documentation.
BeksOmega Feb 7, 2019
c8fce12
Fixed up inline docs.
BeksOmega Feb 7, 2019
9f1a588
Cleaned up comments.
BeksOmega Feb 12, 2019
6fce504
Fixed zoomCenter not actually zooming towards the center.
BeksOmega Feb 12, 2019
98ae73f
Fixed zoomControls error on unmovable bottom-toolbox workspaces
BeksOmega Feb 12, 2019
a4b68e2
Fixed programatically placing blocks in an unmovable workspace.
BeksOmega Feb 12, 2019
a3ee2ce
Removed unnecessary translate call in inject.
BeksOmega Feb 12, 2019
6a5eee1
Reversed removal of translate. (apparently it was necessary)
BeksOmega Feb 12, 2019
99166ba
Cleaned up code in response to first round of reviews.
BeksOmega Feb 13, 2019
b6374fc
Added unit comments to the zoom function.
BeksOmega Feb 13, 2019
c2b496c
Removed bumpObjectsEventChecker. Added BUMP_EVENTS list to Blockly.Ev…
BeksOmega Feb 13, 2019
710e18a
Changed getWorkspaceObjectMetrics call to getBoundingRectangle().
BeksOmega Feb 13, 2019
82a7cdb
Fixed utils.mouseToSvg (was causing problems with zoom on wheel if th…
BeksOmega Feb 14, 2019
41c8107
Fixed zoom when page is scrolled (actually this time). Reverted chang…
BeksOmega Feb 15, 2019
1308469
Fixed centerOnBlock.
BeksOmega Feb 16, 2019
6c75420
Added unit docs to translate. Moved setting the grid position to the …
BeksOmega Feb 16, 2019
94d326d
Added TODO's.
BeksOmega Feb 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions blocks/procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,12 @@ Blockly.Blocks['procedures_callnoreturn'] = {
* @this Blockly.Block
*/
customContextMenu: function(options) {
if (!this.workspace.isMovable()) {
// If we center on the block and the workspace isn't movable we could
// loose blocks at the edges of the workspace.
return;
}

var option = {enabled: true};
option.text = Blockly.Msg['PROCEDURES_HIGHLIGHT_DEF'];
var name = this.getProcedureCall();
Expand Down
15 changes: 15 additions & 0 deletions core/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@ Blockly.Events.COMMENT_MOVE = 'comment_move';
*/
Blockly.Events.FINISHED_LOADING = 'finished_loading';

/**
* List of events that cause objects to be bumped back into the visible
* portion of the workspace (only used for non-movable workspaces).
*
* Not to be confused with bumping so that disconnected connections to do
* not appear connected.
* @const
*/
Blockly.Events.BUMP_EVENTS = [
Blockly.Events.BLOCK_CREATE,
Blockly.Events.BLOCK_MOVE,
Blockly.Events.COMMENT_CREATE,
Blockly.Events.COMMENT_MOVE
];

/**
* List of events queued for firing.
* @private
Expand Down
175 changes: 117 additions & 58 deletions core/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,71 +232,111 @@ Blockly.createMainWorkspace_ = function(svg, options, blockDragSurface,
mainWorkspace.translate(0, 0);
Blockly.mainWorkspace = mainWorkspace;

if (!options.readOnly && !options.hasScrollbars) {
var workspaceChanged = function(e) {
if (!mainWorkspace.isDragging()) {
var metrics = mainWorkspace.getMetrics();
var edgeLeft = metrics.viewLeft + metrics.absoluteLeft;
var edgeTop = metrics.viewTop + metrics.absoluteTop;
if (metrics.contentTop < edgeTop ||
metrics.contentTop + metrics.contentHeight >
metrics.viewHeight + edgeTop ||
metrics.contentLeft <
(options.RTL ? metrics.viewLeft : edgeLeft) ||
metrics.contentLeft + metrics.contentWidth > (options.RTL ?
metrics.viewWidth : metrics.viewWidth + edgeLeft)) {
// One or more blocks may be out of bounds. Bump them back in.
var MARGIN = 25;
var blocks = mainWorkspace.getTopBlocks(false);
if (!options.readOnly && !mainWorkspace.isMovable()) {
// Helper functions for the workspaceChanged callback.
var getWorkspaceMetrics = function() {
var workspaceMetrics = Object.create(null);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it's doing a lot of work and checks that normally happen in getMetrics. Why is all the new math here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bumpObjects function needs a pretty uncommon set of metrics to work properly:

A) It needs everything in workspace units rather than pixel units.
B) It needs the un-bounded size of the content, even if the content is bounded.

Because the needs were so specific to the bumpObjects function, I wrote everything inside inject, but you're right it is more of a workspace thing. (Also I couldn't come up with a good name, getMetricsUnboundedWorkspaceUnits is a bit longwinded)

I can:
A) Leave as is.
B) Add a new function to the workspace_svg, but I would appreciate a name suggestion.
C) Change the signature of getTopLevelWorkspaceMetrics_() to (bool opt_workspaceUnits, bool opt_forceUnbounded), although this might be a bit confusing because I believe the signature of getMetrics would change for flyouts vs workspaces.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave as-is for this change, and add a TODO + github isue to circle back and see if we can move most of it out of inject.

var defaultMetrics = mainWorkspace.getMetrics();
var scale = mainWorkspace.scale;

// Get the view metrics in workspace units.
workspaceMetrics.viewLeft = defaultMetrics.viewLeft / scale;
workspaceMetrics.viewTop = defaultMetrics.viewTop / scale;
workspaceMetrics.viewRight =
(defaultMetrics.viewLeft + defaultMetrics.viewWidth) / scale;
workspaceMetrics.viewBottom =
(defaultMetrics.viewTop + defaultMetrics.viewHeight) / scale;

// Get the exact content metrics (in workspace units), even if the
// content is bounded.
if (mainWorkspace.isContentBounded()) {
// Already in workspace units, no need to divide by scale.
var blocksBoundingBox = mainWorkspace.getBlocksBoundingBox();
workspaceMetrics.contentLeft = blocksBoundingBox.x;
workspaceMetrics.contentTop = blocksBoundingBox.y;
workspaceMetrics.contentRight =
blocksBoundingBox.x + blocksBoundingBox.width;
workspaceMetrics.contentBottom =
blocksBoundingBox.y + blocksBoundingBox.height;
} else {
workspaceMetrics.contentLeft = defaultMetrics.contentLeft / scale;
workspaceMetrics.contentTop = defaultMetrics.contentTop / scale;
workspaceMetrics.contentRight =
(defaultMetrics.contentLeft + defaultMetrics.contentWidth) / scale;
workspaceMetrics.contentBottom =
(defaultMetrics.contentTop + defaultMetrics.contentHeight) / scale;
}

return workspaceMetrics;
};

var bumpObjects = function(e) {
// We always check isMovable_ again because the original
// "not movable" state of isMovable_ could have been changed.
if (!mainWorkspace.isDragging() && !mainWorkspace.isMovable() &&
(Blockly.Events.BUMP_EVENTS.indexOf(e.type) != -1)) {
var metrics = getWorkspaceMetrics();
if (metrics.contentTop < metrics.viewTop ||
metrics.contentBottom > metrics.viewBottom ||
metrics.contentLeft < metrics.viewLeft ||
metrics.contentRight > metrics.viewRight) {

// Handle undo.
var oldGroup = null;
if (e) {
oldGroup = Blockly.Events.getGroup();
Blockly.Events.setGroup(e.group);
}
var movedBlocks = false;
for (var b = 0, block; block = blocks[b]; b++) {
var blockXY = block.getRelativeToSurfaceXY();
var blockHW = block.getHeightWidth();
// Bump any block that's above the top back inside.
var overflowTop = edgeTop + MARGIN - blockHW.height - blockXY.y;
if (overflowTop > 0) {
block.moveBy(0, overflowTop);
movedBlocks = true;
}
// Bump any block that's below the bottom back inside.
var overflowBottom =
edgeTop + metrics.viewHeight - MARGIN - blockXY.y;
if (overflowBottom < 0) {
block.moveBy(0, overflowBottom);
movedBlocks = true;
}
// Bump any block that's off the left back inside.
var overflowLeft = MARGIN + edgeLeft -
blockXY.x - (options.RTL ? 0 : blockHW.width);
if (overflowLeft > 0) {
block.moveBy(overflowLeft, 0);
movedBlocks = true;
}
// Bump any block that's off the right back inside.
var overflowRight = edgeLeft + metrics.viewWidth - MARGIN -
blockXY.x + (options.RTL ? blockHW.width : 0);
if (overflowRight < 0) {
block.moveBy(overflowRight, 0);
movedBlocks = true;
}

switch (e.type) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should a getObjectById() function be added to the workspace so that we can remove this switch statement? Or do you think that's unnecessary?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's unnecessary--blocks and comments share a few APIs, but not many, so you'd still usually have to check the type of the object that you got back.

case Blockly.Events.BLOCK_CREATE:
case Blockly.Events.BLOCK_MOVE:
var object = mainWorkspace.getBlockById(e.blockId);
break;
case Blockly.Events.COMMENT_CREATE:
case Blockly.Events.COMMENT_MOVE:
var object = mainWorkspace.getCommentById(e.commentId);
break;
}
var objectMetrics = object.getBoundingRectangle();

// Bump any object that's above the top back inside.
var overflowTop = metrics.viewTop - objectMetrics.topLeft.y;
if (overflowTop > 0) {
object.moveBy(0, overflowTop);
}

// Bump any object that's below the bottom back inside.
var overflowBottom = metrics.viewBottom - objectMetrics.bottomRight.y;
if (overflowBottom < 0) {
object.moveBy(0, overflowBottom);
}

// Bump any object that's off the left back inside.
var overflowLeft = metrics.viewLeft - objectMetrics.topLeft.x;
if (overflowLeft > 0) {
object.moveBy(overflowLeft, 0);
}

// Bump any object that's off the right back inside.
var overflowRight = metrics.viewRight - objectMetrics.bottomRight.x;
if (overflowRight < 0) {
object.moveBy(overflowRight, 0);
}

if (e) {
if (!e.group && movedBlocks) {
console.log('WARNING: Moved blocks in bounds but there was no event group.'
+ ' This may break undo.');
if (!e.group) {
console.log('WARNING: Moved object in bounds but there was no' +
' event group. This may break undo.');
}
Blockly.Events.setGroup(oldGroup);
}
}
}
};
mainWorkspace.addChangeListener(workspaceChanged);
mainWorkspace.addChangeListener(bumpObjects);
}

// The SVG is now fully assembled.
Blockly.svgResize(mainWorkspace);
Blockly.WidgetDiv.createDom();
Expand Down Expand Up @@ -339,12 +379,6 @@ Blockly.init_ = function(mainWorkspace) {
mainWorkspace.flyout_.init(mainWorkspace);
mainWorkspace.flyout_.show(options.languageTree.childNodes);
mainWorkspace.flyout_.scrollToStart();
// Translate the workspace sideways to avoid the fixed flyout.
mainWorkspace.scrollX = mainWorkspace.flyout_.width_;
if (options.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) {
mainWorkspace.scrollX *= -1;
}
mainWorkspace.translate(mainWorkspace.scrollX, 0);
}
}

Expand All @@ -356,9 +390,31 @@ Blockly.init_ = function(mainWorkspace) {
mainWorkspace.zoomControls_.init(verticalSpacing);
}

if (options.hasScrollbars) {
if (options.moveOptions && options.moveOptions.scrollbars) {
mainWorkspace.scrollbar = new Blockly.ScrollbarPair(mainWorkspace);
mainWorkspace.scrollbar.resize();
} else {
mainWorkspace.setMetrics({x: .5, y: .5});
}

if (mainWorkspace.flyout_) {
// Translate the workspace sideways to avoid the fixed flyout.
switch (mainWorkspace.toolboxPosition) {
case Blockly.TOOLBOX_AT_LEFT:
mainWorkspace.scrollX =
mainWorkspace.RTL ? 0 : mainWorkspace.flyout_.width_;
break;
case Blockly.TOOLBOX_AT_RIGHT:
mainWorkspace.scrollX =
mainWorkspace.RTL ? -mainWorkspace.flyout_.width_ : 0;
break;
case Blockly.TOOLBOX_AT_TOP:
mainWorkspace.scrollY = mainWorkspace.flyout_.height_;
break;
// If the toolbox is at the top left (workspace origin) is untouched,
// so no need to include it.
}
mainWorkspace.translate(mainWorkspace.scrollX, mainWorkspace.scrollY);
}

// Load the sounds.
Expand All @@ -380,6 +436,9 @@ Blockly.init_ = function(mainWorkspace) {
*/
Blockly.inject.bindDocumentEvents_ = function() {
if (!Blockly.documentEventsBound_) {
Blockly.bindEventWithChecks_(document, 'scroll', null,
Blockly.mainWorkspace.updateInverseScreenCTM
.bind(Blockly.mainWorkspace));
Blockly.bindEventWithChecks_(document, 'keydown', null, Blockly.onKeyDown_);
// longStop needs to run to stop the context menu from showing up. It
// should run regardless of what other touch event handlers have run.
Expand Down
42 changes: 37 additions & 5 deletions core/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,6 @@ Blockly.Options = function(options) {
Blockly.TOOLBOX_AT_RIGHT : Blockly.TOOLBOX_AT_LEFT;
}

var hasScrollbars = options['scrollbars'];
if (hasScrollbars === undefined) {
hasScrollbars = hasCategories;
}
var hasCss = options['css'];
if (hasCss === undefined) {
hasCss = true;
Expand Down Expand Up @@ -135,7 +131,9 @@ Blockly.Options = function(options) {
this.maxInstances = options['maxInstances'];
this.pathToMedia = pathToMedia;
this.hasCategories = hasCategories;
this.hasScrollbars = hasScrollbars;
this.moveOptions = Blockly.Options.parseMoveOptions(options, hasCategories);
/** @deprecated January 2019 */
this.hasScrollbars = this.moveOptions.scrollbars;
this.hasTrashcan = hasTrashcan;
this.maxTrashcanContents = maxTrashcanContents;
this.hasSounds = hasSounds;
Expand Down Expand Up @@ -165,6 +163,40 @@ Blockly.Options.prototype.setMetrics = null;
*/
Blockly.Options.prototype.getMetrics = null;

/**
* Parse the user-specified move options, using reasonable defaults where
* behavior is unspecified.
* @param {!Object} options Dictionary of options.
* @param {!boolean} hasCategories Whether the workspace has categories or not.
* @return {!Object} A dictionary of normalized options.
* @private
*/
Blockly.Options.parseMoveOptions = function(options, hasCategories) {
var move = options['move'] || {};
var moveOptions = {};
if (move['scrollbars'] === undefined
&& options['scrollbars'] === undefined) {
moveOptions.scrollbars = hasCategories;
} else {
moveOptions.scrollbars = !!move['scrollbars'] || !!options['scrollbars'];
}
if (!moveOptions.scrollbars || move['wheel'] === undefined) {
// Defaults to false so that developers' settings don't appear to change.
moveOptions.wheel = false;
} else {
moveOptions.wheel = !!move['wheel'];
}
if (!moveOptions.scrollbars) {
moveOptions.drag = false;
} else if (move['drag'] === undefined) {
// Defaults to true if scrollbars is true.
moveOptions.drag = true;
} else {
moveOptions.drag = !!move['drag'];
}
return moveOptions;
};

/**
* Parse the user-specified zoom options, using reasonable defaults where
* behaviour is unspecified. See zoom documentation:
Expand Down
35 changes: 1 addition & 34 deletions core/workspace_dragger.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,6 @@ Blockly.WorkspaceDragger = function(workspace) {
*/
this.workspace_ = workspace;

/**
* The workspace's metrics object at the beginning of the drag. Contains size
* and position metrics of a workspace.
* Coordinate system: pixel coordinates.
* @type {!Object}
* @private
*/
this.startDragMetrics_ = workspace.getMetrics();

/**
* The scroll position of the workspace at the beginning of the drag.
* Coordinate system: pixel coordinates.
Expand Down Expand Up @@ -102,30 +93,6 @@ Blockly.WorkspaceDragger.prototype.endDrag = function(currentDragDeltaXY) {
* @package
*/
Blockly.WorkspaceDragger.prototype.drag = function(currentDragDeltaXY) {
var metrics = this.startDragMetrics_;
var newXY = goog.math.Coordinate.sum(this.startScrollXY_, currentDragDeltaXY);

// Bound the new XY based on workspace bounds.
var x = Math.min(newXY.x, -metrics.contentLeft);
var y = Math.min(newXY.y, -metrics.contentTop);
x = Math.max(x, metrics.viewWidth - metrics.contentLeft -
metrics.contentWidth);
y = Math.max(y, metrics.viewHeight - metrics.contentTop -
metrics.contentHeight);

x = -x - metrics.contentLeft;
y = -y - metrics.contentTop;

this.updateScroll_(x, y);
};

/**
* Move the scrollbars to drag the workspace.
* x and y are in pixels.
* @param {number} x The new x position to move the scrollbar to.
* @param {number} y The new y position to move the scrollbar to.
* @private
*/
Blockly.WorkspaceDragger.prototype.updateScroll_ = function(x, y) {
this.workspace_.scrollbar.set(x, y);
this.workspace_.scroll(newXY.x, newXY.y);
};
Loading