From 43a17bdaa816eaffd307ad24cbda125bc55e6584 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 5 Oct 2017 10:24:20 -0400 Subject: [PATCH 1/5] Add say/think functionality to looks blocks --- src/blocks/scratch3_looks.js | 164 +++++++++++++++++++++++++++++++++-- 1 file changed, 157 insertions(+), 7 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index ca9dac2bd..6235e2ecf 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -1,4 +1,15 @@ const Cast = require('../util/cast'); +const Clone = require('../util/clone'); + +const RenderedTarget = require('../sprites/rendered-target'); + +/** + * @typedef {object} BubbleState - the bubble state associated with a particular target. + * @property {Boolean} onSpriteRight - tracks whether the bubble is right or left of the sprite. + * @property {?int} drawableId - the ID of the associated bubble Drawable, null if none. + * @property {string} text - the text of the bubble. + * @property {string} type - the type of the bubble, "say" or "think" + */ class Scratch3LooksBlocks { constructor (runtime) { @@ -7,6 +18,144 @@ class Scratch3LooksBlocks { * @type {Runtime} */ this.runtime = runtime; + + this._onTargetMoved = this._onTargetMoved.bind(this); + } + + /** + * The default bubble state, to be used when a target has no existing bubble state. + * @type {BubbleState} + */ + static get DEFAULT_BUBBLE_STATE () { + return { + drawableId: null, + onSpriteRight: true, + skinId: null, + text: '', + type: 'say' + }; + } + + /** + * The key to load & store a target's bubble-related state. + * @type {string} + */ + static get STATE_KEY () { + return 'Scratch.looks'; + } + + /** + * @param {Target} target - collect bubble state for this target. Probably, but not necessarily, a RenderedTarget. + * @returns {BubbleState} the mutable bubble state associated with that target. This will be created if necessary. + * @private + */ + _getBubbleState (target) { + let bubbleState = target.getCustomState(Scratch3LooksBlocks.STATE_KEY); + if (!bubbleState) { + bubbleState = Clone.simple(Scratch3LooksBlocks.DEFAULT_BUBBLE_STATE); + target.setCustomState(Scratch3LooksBlocks.STATE_KEY, bubbleState); + } + return bubbleState; + } + + /** + * @param {Target} target - collect bubble state for this target. Probably, but not necessarily, a RenderedTarget. + * @private + */ + _resetBubbleState (target) { + const defaultBubbleState = Clone.simple(Scratch3LooksBlocks.DEFAULT_BUBBLE_STATE); + target.setCustomState(Scratch3LooksBlocks.STATE_KEY, defaultBubbleState); + } + + /** + * Handle a target which has moved. This only fires when the bubble is visible. + * @param {RenderedTarget} target - the target which has moved. + * @private + */ + _onTargetMoved (target) { + const bubbleState = this._getBubbleState(target); + + if (bubbleState.drawableId) { + this._checkBubbleBounds(target); + this._positionBubble(target); + } + } + + _positionBubble (target) { + const bubbleState = this._getBubbleState(target); + const [bubbleWidth, bubbleHeight] = this.runtime.renderer.getSkinSize(bubbleState.drawableId); + const targetBounds = target.getBounds(); + const stageBounds = this.runtime.getTargetForStage().getBounds(); + + this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { + position: [ + bubbleState.onSpriteRight ? targetBounds.right : targetBounds.left - bubbleWidth, + Math.min(stageBounds.top, targetBounds.top + bubbleHeight) + ] + }); + + this.runtime.requestRedraw(); + } + + _checkBubbleBounds (target) { + const bubbleState = this._getBubbleState(target); + const [bubbleWidth, _] = this.runtime.renderer.getSkinSize(bubbleState.drawableId); + const targetBounds = target.getBounds(); + const stageBounds = this.runtime.getTargetForStage().getBounds(); + if (bubbleState.onSpriteRight && bubbleWidth + targetBounds.right > stageBounds.right) { + bubbleState.onSpriteRight = false; + this._renderBubble(target); + } else if (!bubbleState.onSpriteRight && targetBounds.left - bubbleWidth < stageBounds.left) { + bubbleState.onSpriteRight = true; + this._renderBubble(target); + } + } + + _renderBubble (target) { + const bubbleState = this._getBubbleState(target); + const {type, text, onSpriteRight} = bubbleState; + + if (bubbleState.skinId) { + this.runtime.renderer.updateTextSkin(bubbleState.skinId, type, text, onSpriteRight, [0, 0]); + } else { + target.addListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); + + bubbleState.drawableId = this.runtime.renderer.createDrawable(); + bubbleState.skinId = this.runtime.renderer.createTextSkin(type, text, bubbleState.onSpriteRight, [0, 0]); + + const order = this.runtime.renderer.getDrawableOrder(target.drawableID); + this.runtime.renderer.setDrawableOrder(bubbleState.drawableId, order + 1); + this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { + skinId: bubbleState.skinId + }); + + this._checkBubbleBounds(target); + } + + this._positionBubble(target); + } + _updateBubble (target, type, text) { + const bubbleState = this._getBubbleState(target); + bubbleState.type = type; + bubbleState.text = text; + + this._renderBubble(target); + } + + _clearBubble (target) { + const bubbleState = this._getBubbleState(target); + if (bubbleState.drawableId) { + this.runtime.renderer.destroyDrawable(bubbleState.drawableId); + } + if (bubbleState.drawableId) { + this.runtime.renderer.destroySkin(bubbleState.skinId); + } + this._resetBubbleState(target); + + // @TODO is this safe? It could have been already removed? + target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); + + this.runtime.requestRedraw(); } /** @@ -18,7 +167,7 @@ class Scratch3LooksBlocks { looks_say: this.say, looks_sayforsecs: this.sayforsecs, looks_think: this.think, - looks_thinkforsecs: this.sayforsecs, + looks_thinkforsecs: this.thinkforsecs, looks_show: this.show, looks_hide: this.hide, looks_switchcostumeto: this.switchCostume, @@ -41,30 +190,31 @@ class Scratch3LooksBlocks { } say (args, util) { - util.target.setSay('say', args.MESSAGE); + // @TODO in 2.0 calling say/think resets the right/left bias of the bubble + this._updateBubble(util.target, 'say', args.MESSAGE); } sayforsecs (args, util) { - util.target.setSay('say', args.MESSAGE); + this.say(args, util); return new Promise(resolve => { setTimeout(() => { // Clear say bubble and proceed. - util.target.setSay(); + this._clearBubble(util.target); resolve(); }, 1000 * args.SECS); }); } think (args, util) { - util.target.setSay('think', args.MESSAGE); + this._updateBubble(util.target, 'think', args.MESSAGE); } thinkforsecs (args, util) { - util.target.setSay('think', args.MESSAGE); + this.think(args, util); return new Promise(resolve => { setTimeout(() => { // Clear say bubble and proceed. - util.target.setSay(); + this._clearBubble(util.target); resolve(); }, 1000 * args.SECS); }); From 43d061227c0eb63cc268cf18bccf72d34cf307b5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 5 Oct 2017 17:03:30 -0400 Subject: [PATCH 2/5] WIP --- src/blocks/scratch3_looks.js | 63 +++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 6235e2ecf..99ec704b6 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -1,6 +1,6 @@ const Cast = require('../util/cast'); const Clone = require('../util/clone'); - +const Runtime = require('../engine/runtime'); const RenderedTarget = require('../sprites/rendered-target'); /** @@ -20,6 +20,11 @@ class Scratch3LooksBlocks { this.runtime = runtime; this._onTargetMoved = this._onTargetMoved.bind(this); + this._onResetBubbles = this._onResetBubbles.bind(this); + + // Reset all bubbles on start/stop + this.runtime.on('PROJECT_RUN_START', this._onResetBubbles); + this.runtime.on('PROJECT_RUN_STOP', this._onResetBubbles); } /** @@ -32,7 +37,8 @@ class Scratch3LooksBlocks { onSpriteRight: true, skinId: null, text: '', - type: 'say' + type: 'say', + visible: true }; } @@ -81,6 +87,16 @@ class Scratch3LooksBlocks { } } + _onResetBubbles () { + // for (let n = 0; n < this.runtime.targets.length; n++) { + // const target = this.runtime.targets[n]; + // const bubbleState = this._getBubbleState(target); + // if (bubbleState.drawableId) { + // this._clearBubble(target); + // } + // } + } + _positionBubble (target) { const bubbleState = this._getBubbleState(target); const [bubbleWidth, bubbleHeight] = this.runtime.renderer.getSkinSize(bubbleState.drawableId); @@ -116,15 +132,27 @@ class Scratch3LooksBlocks { const {type, text, onSpriteRight} = bubbleState; if (bubbleState.skinId) { + if (!bubbleState.visible) { + bubbleState.visible = true; + this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { + visible: bubbleState.visible + }); + } this.runtime.renderer.updateTextSkin(bubbleState.skinId, type, text, onSpriteRight, [0, 0]); } else { target.addListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); + // TODO is there a way to figure out before rendering whether to default left or right? + const targetBounds = target.getBounds(); + const stageBounds = this.runtime.getTargetForStage().getBounds(); + if (targetBounds.right + 170 > stageBounds.right) { + bubbleState.onSpriteRight = false; + } + bubbleState.drawableId = this.runtime.renderer.createDrawable(); bubbleState.skinId = this.runtime.renderer.createTextSkin(type, text, bubbleState.onSpriteRight, [0, 0]); - const order = this.runtime.renderer.getDrawableOrder(target.drawableID); - this.runtime.renderer.setDrawableOrder(bubbleState.drawableId, order + 1); + this.runtime.renderer.setDrawableOrder(bubbleState.drawableId, Infinity); this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { skinId: bubbleState.skinId }); @@ -134,23 +162,26 @@ class Scratch3LooksBlocks { this._positionBubble(target); } - _updateBubble (target, type, text) { - const bubbleState = this._getBubbleState(target); - bubbleState.type = type; - bubbleState.text = text; - this._renderBubble(target); + _updateBubble (target, type, text) { + // Say/think empty string should clear any bubble + if (text === '') { + this._clearBubble(target); + } else { + const bubbleState = this._getBubbleState(target); + bubbleState.type = type; + bubbleState.text = text; + + this._renderBubble(target); + } } _clearBubble (target) { const bubbleState = this._getBubbleState(target); - if (bubbleState.drawableId) { - this.runtime.renderer.destroyDrawable(bubbleState.drawableId); - } - if (bubbleState.drawableId) { - this.runtime.renderer.destroySkin(bubbleState.skinId); - } - this._resetBubbleState(target); + bubbleState.visible = false; + this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { + visible: bubbleState.visible + }); // @TODO is this safe? It could have been already removed? target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); From 14600d78935fe8762f792535a4465ab7a41da909 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 6 Oct 2017 13:43:07 -0400 Subject: [PATCH 3/5] Update looks implementation --- src/blocks/scratch3_looks.js | 90 +++++++++++++++++++++++++++--------- src/engine/runtime.js | 28 ++++++++++- src/sprites/sprite.js | 1 + 3 files changed, 96 insertions(+), 23 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 99ec704b6..449bfd05a 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -21,10 +21,11 @@ class Scratch3LooksBlocks { this._onTargetMoved = this._onTargetMoved.bind(this); this._onResetBubbles = this._onResetBubbles.bind(this); + this._onTargetWillExit = this._onTargetWillExit.bind(this); // Reset all bubbles on start/stop - this.runtime.on('PROJECT_RUN_START', this._onResetBubbles); - this.runtime.on('PROJECT_RUN_STOP', this._onResetBubbles); + this.runtime.on('PROJECT_STOP_ALL', this._onResetBubbles); + this.runtime.on('targetWasRemoved', this._onTargetWillExit); } /** @@ -74,29 +75,54 @@ class Scratch3LooksBlocks { } /** - * Handle a target which has moved. This only fires when the bubble is visible. + * Handle a target which has moved. * @param {RenderedTarget} target - the target which has moved. * @private */ _onTargetMoved (target) { const bubbleState = this._getBubbleState(target); - if (bubbleState.drawableId) { + if (bubbleState.drawableId && bubbleState.visible) { this._checkBubbleBounds(target); this._positionBubble(target); } } - _onResetBubbles () { - // for (let n = 0; n < this.runtime.targets.length; n++) { - // const target = this.runtime.targets[n]; - // const bubbleState = this._getBubbleState(target); - // if (bubbleState.drawableId) { - // this._clearBubble(target); - // } - // } + /** + * Handle a target which has moved. + * @param {RenderedTarget} target - the target which has moved. + * @private + */ + _onTargetWillExit (target) { + const bubbleState = this._getBubbleState(target); + if (bubbleState.drawableId) { + this.runtime.renderer.destroyDrawable(bubbleState.drawableId); + } + if (bubbleState.skinId) { + this.runtime.renderer.destroySkin(bubbleState.skinId); + } + } + /** + * Handle project start/stop by clearing all visible bubbles. + * @private + */ + _onResetBubbles () { + for (let n = 0; n < this.runtime.targets.length; n++) { + const target = this.runtime.targets[n]; + const bubbleState = this._getBubbleState(target); + if (bubbleState.drawableId) { + this._clearBubble(target); + } + } + } + + /** + * Position the bubble of a target. + * @param {!Target} target Target whose bubble needs positioning. + * @private + */ _positionBubble (target) { const bubbleState = this._getBubbleState(target); const [bubbleWidth, bubbleHeight] = this.runtime.renderer.getSkinSize(bubbleState.drawableId); @@ -113,6 +139,12 @@ class Scratch3LooksBlocks { this.runtime.requestRedraw(); } + /** + * Check whether a bubble needs to be flipped. If so, flip the `onSpriteRight` state + * and call the bubble render again. + * @param {!Target} target Target whose bubble needs positioning. + * @private + */ _checkBubbleBounds (target) { const bubbleState = this._getBubbleState(target); const [bubbleWidth, _] = this.runtime.renderer.getSkinSize(bubbleState.drawableId); @@ -127,6 +159,13 @@ class Scratch3LooksBlocks { } } + /** + * Create a visible bubble for a target. If a bubble exists for the target, + * just set it to visible and update the type/text. Otherwise create a new + * bubble and update the relevant custom state. + * @param {!Target} target Target who needs a bubble. + * @private + */ _renderBubble (target) { const bubbleState = this._getBubbleState(target); const {type, text, onSpriteRight} = bubbleState; @@ -163,29 +202,38 @@ class Scratch3LooksBlocks { this._positionBubble(target); } + /** + * The entry point for say/think blocks. Clears existing bubble if the text is empty. + * Set the bubble custom state and then call _renderBubble. + * @param {!Target} target Target that say/think blocks are being called on. + * @param {!string} type Either "say" or "think" + * @param {!string} text The text for the bubble, empty string clears the bubble. + * @private + */ _updateBubble (target, type, text) { - // Say/think empty string should clear any bubble if (text === '') { this._clearBubble(target); } else { const bubbleState = this._getBubbleState(target); bubbleState.type = type; bubbleState.text = text; - this._renderBubble(target); } } + /** + * Hide the bubble for a given target. + * @param {!Target} target Target that say/think blocks are being called on. + * @private + */ _clearBubble (target) { const bubbleState = this._getBubbleState(target); bubbleState.visible = false; - this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { - visible: bubbleState.visible - }); - - // @TODO is this safe? It could have been already removed? - target.removeListener(RenderedTarget.EVENT_TARGET_MOVED, this._onTargetMoved); - + if (bubbleState.drawableId) { + this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { + visible: bubbleState.visible + }); + } this.runtime.requestRedraw(); } diff --git a/src/engine/runtime.js b/src/engine/runtime.js index a36081d60..dc3f7c128 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -280,7 +280,8 @@ class Runtime extends EventEmitter { } /** - * Event name for glowing the green flag + * Event name when threads start running. + * Used by the UI to indicate running status. * @const {string} */ static get PROJECT_RUN_START () { @@ -288,13 +289,23 @@ class Runtime extends EventEmitter { } /** - * Event name for unglowing the green flag + * Event name when threads stop running + * Used by the UI to indicate not-running status. * @const {string} */ static get PROJECT_RUN_STOP () { return 'PROJECT_RUN_STOP'; } + /** + * Event name for project being stopped or restarted by the user. + * Used by blocks that need to reset state. + * @const {string} + */ + static get PROJECT_STOP_ALL () { + return 'PROJECT_STOP_ALL'; + } + /** * Event name for visual value report. * @const {string} @@ -905,6 +916,9 @@ class Runtime extends EventEmitter { * Stop "everything." */ stopAll () { + // Emit stop event to allow blocks to clean up any state. + this.emit(Runtime.PROJECT_STOP_ALL); + // Dispose all clones. const newTargets = []; for (let i = 0; i < this.targets.length; i++) { @@ -1227,6 +1241,16 @@ class Runtime extends EventEmitter { this.emit('targetWasCreated', newTarget, sourceTarget); } + /** + * Report that a new target has been created, possibly by cloning an existing target. + * @param {Target} newTarget - the newly created target. + * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any. + * @fires Runtime#targetWasCreated + */ + fireTargetWasRemoved (newTarget, sourceTarget) { + this.emit('targetWasRemoved', newTarget, sourceTarget); + } + /** * Get a target representing the Scratch stage, if one exists. * @return {?Target} The target, if found. diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js index ee5b9394c..268184343 100644 --- a/src/sprites/sprite.js +++ b/src/sprites/sprite.js @@ -71,6 +71,7 @@ class Sprite { * @param {!RenderedTarget} clone - the clone to be removed. */ removeClone (clone) { + this.runtime.fireTargetWasRemoved(clone); const cloneIndex = this.clones.indexOf(clone); if (cloneIndex >= 0) { this.clones.splice(cloneIndex, 1); From 9af9eb1d926b5140d6acb95ab7d6febae92e3054 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 6 Oct 2017 15:24:29 -0400 Subject: [PATCH 4/5] Fix hide/show --- src/blocks/scratch3_looks.js | 87 +++++++++++++----------------------- 1 file changed, 30 insertions(+), 57 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 449bfd05a..9061a008f 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -1,6 +1,5 @@ const Cast = require('../util/cast'); const Clone = require('../util/clone'); -const Runtime = require('../engine/runtime'); const RenderedTarget = require('../sprites/rendered-target'); /** @@ -9,6 +8,7 @@ const RenderedTarget = require('../sprites/rendered-target'); * @property {?int} drawableId - the ID of the associated bubble Drawable, null if none. * @property {string} text - the text of the bubble. * @property {string} type - the type of the bubble, "say" or "think" + * @property {Boolean} visible - whether the bubble is hidden along with its sprite. */ class Scratch3LooksBlocks { @@ -65,15 +65,6 @@ class Scratch3LooksBlocks { return bubbleState; } - /** - * @param {Target} target - collect bubble state for this target. Probably, but not necessarily, a RenderedTarget. - * @private - */ - _resetBubbleState (target) { - const defaultBubbleState = Clone.simple(Scratch3LooksBlocks.DEFAULT_BUBBLE_STATE); - target.setCustomState(Scratch3LooksBlocks.STATE_KEY, defaultBubbleState); - } - /** * Handle a target which has moved. * @param {RenderedTarget} target - the target which has moved. @@ -81,27 +72,26 @@ class Scratch3LooksBlocks { */ _onTargetMoved (target) { const bubbleState = this._getBubbleState(target); - if (bubbleState.drawableId && bubbleState.visible) { - this._checkBubbleBounds(target); this._positionBubble(target); } } /** - * Handle a target which has moved. - * @param {RenderedTarget} target - the target which has moved. + * Handle a target which is exiting. + * @param {RenderedTarget} target - the target. * @private */ _onTargetWillExit (target) { const bubbleState = this._getBubbleState(target); if (bubbleState.drawableId) { this.runtime.renderer.destroyDrawable(bubbleState.drawableId); + bubbleState.drawableId = null; } if (bubbleState.skinId) { this.runtime.renderer.destroySkin(bubbleState.skinId); + bubbleState.skinId = null; } - } /** @@ -110,16 +100,12 @@ class Scratch3LooksBlocks { */ _onResetBubbles () { for (let n = 0; n < this.runtime.targets.length; n++) { - const target = this.runtime.targets[n]; - const bubbleState = this._getBubbleState(target); - if (bubbleState.drawableId) { - this._clearBubble(target); - } + this._onTargetWillExit(this.runtime.targets[n]); } } /** - * Position the bubble of a target. + * Position the bubble of a target. If it doesn't fit on the specified side, flip and rerender. * @param {!Target} target Target whose bubble needs positioning. * @private */ @@ -128,34 +114,20 @@ class Scratch3LooksBlocks { const [bubbleWidth, bubbleHeight] = this.runtime.renderer.getSkinSize(bubbleState.drawableId); const targetBounds = target.getBounds(); const stageBounds = this.runtime.getTargetForStage().getBounds(); - - this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { - position: [ - bubbleState.onSpriteRight ? targetBounds.right : targetBounds.left - bubbleWidth, - Math.min(stageBounds.top, targetBounds.top + bubbleHeight) - ] - }); - - this.runtime.requestRedraw(); - } - - /** - * Check whether a bubble needs to be flipped. If so, flip the `onSpriteRight` state - * and call the bubble render again. - * @param {!Target} target Target whose bubble needs positioning. - * @private - */ - _checkBubbleBounds (target) { - const bubbleState = this._getBubbleState(target); - const [bubbleWidth, _] = this.runtime.renderer.getSkinSize(bubbleState.drawableId); - const targetBounds = target.getBounds(); - const stageBounds = this.runtime.getTargetForStage().getBounds(); if (bubbleState.onSpriteRight && bubbleWidth + targetBounds.right > stageBounds.right) { bubbleState.onSpriteRight = false; this._renderBubble(target); } else if (!bubbleState.onSpriteRight && targetBounds.left - bubbleWidth < stageBounds.left) { bubbleState.onSpriteRight = true; this._renderBubble(target); + } else { + this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { + position: [ + bubbleState.onSpriteRight ? targetBounds.right : targetBounds.left - bubbleWidth, + Math.min(stageBounds.top, targetBounds.top + bubbleHeight) + ] + }); + this.runtime.requestRedraw(); } } @@ -164,11 +136,15 @@ class Scratch3LooksBlocks { * just set it to visible and update the type/text. Otherwise create a new * bubble and update the relevant custom state. * @param {!Target} target Target who needs a bubble. + * @return {undefined} Early return if text is empty string. * @private */ _renderBubble (target) { const bubbleState = this._getBubbleState(target); const {type, text, onSpriteRight} = bubbleState; + if (text === '') { + return this._setBubbleVisibility(target, false); + } if (bubbleState.skinId) { if (!bubbleState.visible) { @@ -195,8 +171,6 @@ class Scratch3LooksBlocks { this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { skinId: bubbleState.skinId }); - - this._checkBubbleBounds(target); } this._positionBubble(target); @@ -211,24 +185,21 @@ class Scratch3LooksBlocks { * @private */ _updateBubble (target, type, text) { - if (text === '') { - this._clearBubble(target); - } else { - const bubbleState = this._getBubbleState(target); - bubbleState.type = type; - bubbleState.text = text; - this._renderBubble(target); - } + const bubbleState = this._getBubbleState(target); + bubbleState.type = type; + bubbleState.text = text; + this._renderBubble(target); } /** * Hide the bubble for a given target. * @param {!Target} target Target that say/think blocks are being called on. + * @param {!boolean} visibility Visible or not. * @private */ - _clearBubble (target) { + _setBubbleVisibility (target, visibility) { const bubbleState = this._getBubbleState(target); - bubbleState.visible = false; + bubbleState.visible = visibility; if (bubbleState.drawableId) { this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, { visible: bubbleState.visible @@ -278,7 +249,7 @@ class Scratch3LooksBlocks { return new Promise(resolve => { setTimeout(() => { // Clear say bubble and proceed. - this._clearBubble(util.target); + this._updateBubble(util.target, 'say', ''); resolve(); }, 1000 * args.SECS); }); @@ -293,7 +264,7 @@ class Scratch3LooksBlocks { return new Promise(resolve => { setTimeout(() => { // Clear say bubble and proceed. - this._clearBubble(util.target); + this._updateBubble(util.target, 'think', ''); resolve(); }, 1000 * args.SECS); }); @@ -301,10 +272,12 @@ class Scratch3LooksBlocks { show (args, util) { util.target.setVisible(true); + this._renderBubble(util.target); } hide (args, util) { util.target.setVisible(false); + this._setBubbleVisibility(util.target, false); } /** From fa2db053a77c349c20362ba712f4c17f1eb03985 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 11 Oct 2017 14:19:15 -0400 Subject: [PATCH 5/5] Fix copy paste error on targetWasRemoved --- src/engine/runtime.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index dc3f7c128..0c0645f9b 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -1242,13 +1242,12 @@ class Runtime extends EventEmitter { } /** - * Report that a new target has been created, possibly by cloning an existing target. - * @param {Target} newTarget - the newly created target. - * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any. - * @fires Runtime#targetWasCreated + * Report that a clone target is being removed. + * @param {Target} target - the target being removed + * @fires Runtime#targetWasRemoved */ - fireTargetWasRemoved (newTarget, sourceTarget) { - this.emit('targetWasRemoved', newTarget, sourceTarget); + fireTargetWasRemoved (target) { + this.emit('targetWasRemoved', target); } /**