diff --git a/core/events/events_viewport.js b/core/events/events_viewport.js index 43b9b6a8c..6d857cd67 100644 --- a/core/events/events_viewport.js +++ b/core/events/events_viewport.js @@ -28,11 +28,13 @@ goog.require('Blockly.utils.object'); * event. * @param {string=} opt_workspaceId The workspace identifier for this event. * Undefined for a blank event. + * @param {number=} opt_oldScale The old scale of the workspace. Undefined for a + * blank event. * @extends {Blockly.Events.UiBase} * @constructor */ Blockly.Events.ViewportChange = function(opt_top, opt_left, opt_scale, - opt_workspaceId) { + opt_workspaceId, opt_oldScale) { Blockly.Events.ViewportChange.superClass_.constructor.call(this, opt_workspaceId); /** @@ -54,6 +56,12 @@ Blockly.Events.ViewportChange = function(opt_top, opt_left, opt_scale, * @type {number|undefined} */ this.scale = opt_scale; + + /** + * The old scale of the workspace. + * @type {number|undefined} + */ + this.oldScale = opt_oldScale; }; Blockly.utils.object.inherits(Blockly.Events.ViewportChange, Blockly.Events.UiBase); @@ -73,6 +81,7 @@ Blockly.Events.ViewportChange.prototype.toJson = function() { json['viewTop'] = this.viewTop; json['viewLeft'] = this.viewLeft; json['scale'] = this.scale; + json['oldScale'] = this.oldScale; return json; }; @@ -85,6 +94,7 @@ Blockly.Events.ViewportChange.prototype.fromJson = function(json) { this.viewTop = json['viewTop']; this.viewLeft = json['viewLeft']; this.scale = json['scale']; + this.oldScale = json['oldScale']; }; Blockly.registry.register(Blockly.registry.Type.EVENT, diff --git a/core/inject.js b/core/inject.js index db68960ef..f2ec195f7 100644 --- a/core/inject.js +++ b/core/inject.js @@ -245,32 +245,38 @@ Blockly.bumpTopObjectsIntoBounds_ = function(workspace) { Blockly.bumpIntoBoundsHandler_ = function(workspace) { return function(e) { var metricsManager = workspace.getMetricsManager(); - if (!metricsManager.hasFixedEdges() || workspace.isDragging() || - Blockly.Events.BUMP_EVENTS.indexOf(e.type) === -1) { + if (!metricsManager.hasFixedEdges || workspace.isDragging()) { return; } - var scrollMetricsInWsCoords = metricsManager.getScrollMetrics(true); + if (Blockly.Events.BUMP_EVENTS.indexOf(e.type) !== -1) { + var scrollMetricsInWsCoords = metricsManager.getScrollMetrics(true); - // Triggered by move/create event - var object = Blockly.extractObjectFromEvent_(workspace, e); - if (!object) { - return; - } - // Handle undo. - var oldGroup = Blockly.Events.getGroup(); - Blockly.Events.setGroup(e.group); + // Triggered by move/create event + var object = Blockly.extractObjectFromEvent_(workspace, e); + if (!object) { + return; + } + // Handle undo. + var oldGroup = Blockly.Events.getGroup(); + Blockly.Events.setGroup(e.group); - var wasBumped = Blockly.bumpObjectIntoBounds_( - workspace, scrollMetricsInWsCoords, - /** @type {!Blockly.IBoundedElement} */ (object)); + var wasBumped = Blockly.bumpObjectIntoBounds_( + workspace, scrollMetricsInWsCoords, + /** @type {!Blockly.IBoundedElement} */ (object)); - if (wasBumped && !e.group) { - console.warn('Moved object in bounds but there was no' + - ' event group. This may break undo.'); - } - if (oldGroup !== null) { - Blockly.Events.setGroup(oldGroup); + if (wasBumped && !e.group) { + console.warn('Moved object in bounds but there was no' + + ' event group. This may break undo.'); + } + if (oldGroup !== null) { + Blockly.Events.setGroup(oldGroup); + } + } else if (e.type === Blockly.Events.VIEWPORT_CHANGE) { + var viewportEvent = /** @type {!Blockly.Events.ViewportChange} */ (e); + if (viewportEvent.scale > viewportEvent.oldScale) { + Blockly.bumpTopObjectsIntoBounds_(workspace); + } } }; }; diff --git a/core/options.js b/core/options.js index ec707fa22..f8e1ee4cc 100644 --- a/core/options.js +++ b/core/options.js @@ -275,10 +275,10 @@ Blockly.Options.parseMoveOptions_ = function(options, 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; + // Defaults to true if single-direction scroll is enabled. + moveOptions.wheel = typeof moveOptions.scrollbars == 'object'; } else { moveOptions.wheel = !!move['wheel']; } diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 6634cdef5..8bc2d5ded 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -1238,7 +1238,7 @@ Blockly.WorkspaceSvg.prototype.maybeFireViewportChangeEvent = function() { return; } var event = new (Blockly.Events.get(Blockly.Events.VIEWPORT_CHANGE))(top, - left, scale, this.id); + left, scale, this.id, this.oldScale_); this.oldScale_ = scale; this.oldTop_ = top; this.oldLeft_ = left; diff --git a/core/zoom_controls.js b/core/zoom_controls.js index 7123e40ad..8d5e6ac3f 100644 --- a/core/zoom_controls.js +++ b/core/zoom_controls.js @@ -450,9 +450,21 @@ Blockly.ZoomControls.prototype.createZoomResetSvg_ = function(rnd) { */ Blockly.ZoomControls.prototype.resetZoom_ = function(e) { this.workspace_.markFocused(); - this.workspace_.setScale(this.workspace_.options.zoomOptions.startScale); + + // zoom is passed amount and computes the new scale using the formula: + // targetScale = currentScale * Math.pow(speed, amount) + var targetScale = this.workspace_.options.zoomOptions.startScale; + var currentScale = this.workspace_.scale; + var speed = this.workspace_.options.zoomOptions.scaleSpeed; + // To compute amount: + // amount = log(speed, (targetScale / currentScale)) + // Math.log computes natural logarithm (ln), to change the base, use formula: + // log(base, value) = ln(value) / ln(base) + var amount = Math.log(targetScale / currentScale) / Math.log(speed); + this.workspace_.beginCanvasTransition(); - this.workspace_.scrollCenter(); + this.workspace_.zoomCenter(amount); + setTimeout(this.workspace_.endCanvasTransition.bind(this.workspace_), 500); this.fireZoomEvent_(); Blockly.Touch.clearTouchIdentifier(); // Don't block future drags. diff --git a/tests/mocha/event_test.js b/tests/mocha/event_test.js index 66e414e65..26e5d4a09 100644 --- a/tests/mocha/event_test.js +++ b/tests/mocha/event_test.js @@ -551,13 +551,13 @@ suite('Events', function() { getArgs: (thisObj) => [true, thisObj.workspace.id], getExpectedJson: () => ({type: 'trashcan_open', isOpen: true})}, {title: 'Viewport change', class: Blockly.Events.ViewportChange, - getArgs: (thisObj) => [2.666, 1.333, 1.2, thisObj.workspace.id], + getArgs: (thisObj) => [2.666, 1.333, 1.2, thisObj.workspace.id, 1], getExpectedJson: () => ({type: 'viewport_change', viewTop: 2.666, - viewLeft: 1.333, scale: 1.2})}, + viewLeft: 1.333, scale: 1.2, oldScale: 1})}, {title: 'Viewport change (0,0)', class: Blockly.Events.ViewportChange, - getArgs: (thisObj) => [0, 0, 1.2, thisObj.workspace.id], + getArgs: (thisObj) => [0, 0, 1.2, thisObj.workspace.id, 1], getExpectedJson: () => ({type: 'viewport_change', viewTop: 0, - viewLeft: 0, scale: 1.2})}, + viewLeft: 0, scale: 1.2, oldScale: 1})}, ]; var blockEventTestCases = [ {title: 'Block change', class: Blockly.Events.BlockChange, diff --git a/tests/mocha/workspace_svg_test.js b/tests/mocha/workspace_svg_test.js index 84c617495..de07a7fae 100644 --- a/tests/mocha/workspace_svg_test.js +++ b/tests/mocha/workspace_svg_test.js @@ -178,6 +178,7 @@ suite('WorkspaceSvg', function() { var metrics = workspace.getMetrics(); var expectedProperties = { scale: workspace.scale, + oldScale: 1, viewTop: metrics.viewTop, viewLeft: metrics.viewLeft };