From 9af9eb1d926b5140d6acb95ab7d6febae92e3054 Mon Sep 17 00:00:00 2001
From: Paul Kaplan <pkaplan@media.mit.edu>
Date: Fri, 6 Oct 2017 15:24:29 -0400
Subject: [PATCH] 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);
     }
 
     /**