@@ -3,25 +3,23 @@ const twgl = require('twgl.js');
33const  CanvasMeasurementProvider  =  require ( './util/canvas-measurement-provider' ) ; 
44const  Skin  =  require ( './Skin' ) ; 
55
6- const  BubbleStyle  =  { 
7-     MAX_LINE_WIDTH : 170 ,  // Maximum width, in Scratch pixels, of a single line of text 
8- 
9-     MIN_WIDTH : 50 ,  // Minimum width, in Scratch pixels, of a text bubble 
10-     STROKE_WIDTH : 4 ,  // Thickness of the stroke around the bubble. Only half's visible because it's drawn under the fill 
11-     PADDING : 10 ,  // Padding around the text area 
12-     CORNER_RADIUS : 16 ,  // Radius of the rounded corners 
13-     TAIL_HEIGHT : 12 ,  // Height of the speech bubble's "tail". Probably should be a constant. 
14- 
15-     FONT : 'Helvetica' ,  // Font to render the text with 
16-     FONT_SIZE : 14 ,  // Font size, in Scratch pixels 
17-     FONT_HEIGHT_RATIO : 0.9 ,  // Height, in Scratch pixels, of the text, as a proportion of the font's size 
18-     LINE_HEIGHT : 16 ,  // Spacing between each line of text 
19- 
20-     COLORS : { 
21-         BUBBLE_FILL : 'white' , 
22-         BUBBLE_STROKE : 'rgba(0, 0, 0, 0.15)' , 
23-         TEXT_FILL : '#575E75' 
24-     } 
6+ const  DEFAULT_BUBBLE_STYLE  =  { 
7+     maxLineWidth : 170 ,  // Maximum width, in Scratch pixels, of a single line of text 
8+ 
9+     minWidth : 50 ,  // Minimum width, in Scratch pixels, of a text bubble 
10+     strokeWidth : 4 ,  // Thickness of the stroke around the bubble. Only half's visible because it's drawn under the fill 
11+     padding : 10 ,  // Padding around the text area 
12+     cornerRadius : 16 ,  // Radius of the rounded corners 
13+     tailHeight : 12 ,  // Height of the speech bubble's "tail". Probably should be a constant. 
14+ 
15+     font : 'Helvetica' ,  // Font to render the text with 
16+     fontSize : 14 ,  // Font size, in Scratch pixels 
17+     fontHeightRatio : 0.9 ,  // Height, in Scratch pixels, of the text, as a proportion of the font's size 
18+     lineHeight : 16 ,  // Spacing between each line of text 
19+ 
20+     bubbleFill : 'white' , 
21+     bubbleStroke : 'rgba(0, 0, 0, 0.15)' , 
22+     textFill : '#575E75' 
2523} ; 
2624
2725const  MAX_SCALE  =  10 ; 
@@ -64,6 +62,13 @@ class TextBubbleSkin extends Skin {
6462        /** @type  {boolean } */ 
6563        this . _textureDirty  =  true ; 
6664
65+         /** 
66+          * Use setStyle() instead of modfying directly. 
67+          * Supplied values are considered trusted and will not be further checked or sanitized. 
68+          * Updating skin style will not reposition drawables. 
69+          */ 
70+         this . _style  =  DEFAULT_BUBBLE_STYLE ; 
71+ 
6772        this . measurementProvider  =  new  CanvasMeasurementProvider ( this . _canvas . getContext ( '2d' ) ) ; 
6873        this . textWrapper  =  renderer . createTextWrapper ( this . measurementProvider ) ; 
6974
@@ -108,18 +113,33 @@ class TextBubbleSkin extends Skin {
108113        this . emitWasAltered ( ) ; 
109114    } 
110115
116+     /** 
117+      * Change style used for rendering the bubble. Properties not specified will be unchanged. 
118+      * Given argument will be copied internally, so you can freely change it later without 
119+      * affecting the skin. 
120+      * @param  {object } newStyle New styles to be applied. 
121+      */ 
122+     setStyle  ( newStyle )  { 
123+         this . _style  =  Object . assign ( { } ,  this . _style ,  newStyle ) ; 
124+         this . _restyleCanvas ( ) ; 
125+         this . _textDirty  =  true ; 
126+         this . _textureDirty  =  true ; 
127+         this . emitWasAltered ( ) ; 
128+     } 
129+ 
111130    /** 
112131     * Re-style the canvas after resizing it. This is necessary to ensure proper text measurement. 
113132     */ 
114133    _restyleCanvas  ( )  { 
115-         this . _canvas . getContext ( '2d' ) . font  =  `${ BubbleStyle . FONT_SIZE } ${ BubbleStyle . FONT }  ; 
134+         this . measurementProvider . clearCache ( ) ; 
135+         this . _canvas . getContext ( '2d' ) . font  =  `${ this . _style . fontSize } ${ this . _style . font }  ; 
116136    } 
117137
118138    /** 
119139     * Update the array of wrapped lines and the text dimensions. 
120140     */ 
121141    _reflowLines  ( )  { 
122-         this . _lines  =  this . textWrapper . wrapText ( BubbleStyle . MAX_LINE_WIDTH ,  this . _text ) ; 
142+         this . _lines  =  this . textWrapper . wrapText ( this . _style . maxLineWidth ,  this . _text ) ; 
123143
124144        // Measure width of longest line to avoid extra-wide bubbles 
125145        let  longestLineWidth  =  0 ; 
@@ -128,14 +148,14 @@ class TextBubbleSkin extends Skin {
128148        } 
129149
130150        // Calculate the canvas-space sizes of the padded text area and full text bubble 
131-         const  paddedWidth  =  Math . max ( longestLineWidth ,  BubbleStyle . MIN_WIDTH )  +  ( BubbleStyle . PADDING  *  2 ) ; 
132-         const  paddedHeight  =  ( BubbleStyle . LINE_HEIGHT  *  this . _lines . length )  +  ( BubbleStyle . PADDING  *  2 ) ; 
151+         const  paddedWidth  =  Math . max ( longestLineWidth ,  this . _style . minWidth )  +  ( this . _style . padding  *  2 ) ; 
152+         const  paddedHeight  =  ( this . _style . lineHeight  *  this . _lines . length )  +  ( this . _style . padding  *  2 ) ; 
133153
134154        this . _textAreaSize . width  =  paddedWidth ; 
135155        this . _textAreaSize . height  =  paddedHeight ; 
136156
137-         this . _size [ 0 ]  =  paddedWidth  +  BubbleStyle . STROKE_WIDTH ; 
138-         this . _size [ 1 ]  =  paddedHeight  +  BubbleStyle . STROKE_WIDTH  +  BubbleStyle . TAIL_HEIGHT ; 
157+         this . _size [ 0 ]  =  paddedWidth  +  this . _style . strokeWidth ; 
158+         this . _size [ 1 ]  =  paddedHeight  +  this . _style . strokeWidth  +  this . _style . tailHeight ; 
139159
140160        this . _textDirty  =  false ; 
141161    } 
@@ -158,14 +178,13 @@ class TextBubbleSkin extends Skin {
158178        // Resize the canvas to the correct screen-space size 
159179        this . _canvas . width  =  Math . ceil ( this . _size [ 0 ]  *  scale ) ; 
160180        this . _canvas . height  =  Math . ceil ( this . _size [ 1 ]  *  scale ) ; 
161-         this . _restyleCanvas ( ) ; 
162181
163182        // Reset the transform before clearing to ensure 100% clearage 
164183        ctx . setTransform ( 1 ,  0 ,  0 ,  1 ,  0 ,  0 ) ; 
165184        ctx . clearRect ( 0 ,  0 ,  this . _canvas . width ,  this . _canvas . height ) ; 
166185
167186        ctx . scale ( scale ,  scale ) ; 
168-         ctx . translate ( BubbleStyle . STROKE_WIDTH  *  0.5 ,  BubbleStyle . STROKE_WIDTH  *  0.5 ) ; 
187+         ctx . translate ( this . _style . strokeWidth  *  0.5 ,  this . _style . strokeWidth  *  0.5 ) ; 
169188
170189        // If the text bubble points leftward, flip the canvas 
171190        ctx . save ( ) ; 
@@ -176,16 +195,16 @@ class TextBubbleSkin extends Skin {
176195
177196        // Draw the bubble's rounded borders 
178197        ctx . beginPath ( ) ; 
179-         ctx . moveTo ( BubbleStyle . CORNER_RADIUS ,  paddedHeight ) ; 
180-         ctx . arcTo ( 0 ,  paddedHeight ,  0 ,  paddedHeight  -  BubbleStyle . CORNER_RADIUS ,   BubbleStyle . CORNER_RADIUS ) ; 
181-         ctx . arcTo ( 0 ,  0 ,  paddedWidth ,  0 ,  BubbleStyle . CORNER_RADIUS ) ; 
182-         ctx . arcTo ( paddedWidth ,  0 ,  paddedWidth ,  paddedHeight ,  BubbleStyle . CORNER_RADIUS ) ; 
183-         ctx . arcTo ( paddedWidth ,  paddedHeight ,  paddedWidth  -  BubbleStyle . CORNER_RADIUS ,  paddedHeight , 
184-             BubbleStyle . CORNER_RADIUS ) ; 
198+         ctx . moveTo ( this . _style . cornerRadius ,  paddedHeight ) ; 
199+         ctx . arcTo ( 0 ,  paddedHeight ,  0 ,  paddedHeight  -  this . _style . cornerRadius ,   this . _style . cornerRadius ) ; 
200+         ctx . arcTo ( 0 ,  0 ,  paddedWidth ,  0 ,  this . _style . cornerRadius ) ; 
201+         ctx . arcTo ( paddedWidth ,  0 ,  paddedWidth ,  paddedHeight ,  this . _style . cornerRadius ) ; 
202+         ctx . arcTo ( paddedWidth ,  paddedHeight ,  paddedWidth  -  this . _style . cornerRadius ,  paddedHeight , 
203+             this . _style . cornerRadius ) ; 
185204
186205        // Translate the canvas so we don't have to do a bunch of width/height arithmetic 
187206        ctx . save ( ) ; 
188-         ctx . translate ( paddedWidth  -  BubbleStyle . CORNER_RADIUS ,  paddedHeight ) ; 
207+         ctx . translate ( paddedWidth  -  this . _style . cornerRadius ,  paddedHeight ) ; 
189208
190209        // Draw the bubble's "tail" 
191210        if  ( this . _bubbleType  ===  'say' )  { 
@@ -212,9 +231,9 @@ class TextBubbleSkin extends Skin {
212231        // Un-translate the canvas and fill + stroke the text bubble 
213232        ctx . restore ( ) ; 
214233
215-         ctx . fillStyle  =  BubbleStyle . COLORS . BUBBLE_FILL ; 
216-         ctx . strokeStyle  =  BubbleStyle . COLORS . BUBBLE_STROKE ; 
217-         ctx . lineWidth  =  BubbleStyle . STROKE_WIDTH ; 
234+         ctx . fillStyle  =  this . _style . bubbleFill ; 
235+         ctx . strokeStyle  =  this . _style . bubbleStroke ; 
236+         ctx . lineWidth  =  this . _style . strokeWidth ; 
218237
219238        ctx . stroke ( ) ; 
220239        ctx . fill ( ) ; 
@@ -223,16 +242,15 @@ class TextBubbleSkin extends Skin {
223242        ctx . restore ( ) ; 
224243
225244        // Draw each line of text 
226-         ctx . fillStyle  =  BubbleStyle . COLORS . TEXT_FILL ; 
227-         ctx . font  =  `${ BubbleStyle . FONT_SIZE } ${ BubbleStyle . FONT }  ; 
245+         ctx . fillStyle  =  this . _style . textFill ; 
228246        const  lines  =  this . _lines ; 
229247        for  ( let  lineNumber  =  0 ;  lineNumber  <  lines . length ;  lineNumber ++ )  { 
230248            const  line  =  lines [ lineNumber ] ; 
231249            ctx . fillText ( 
232250                line , 
233-                 BubbleStyle . PADDING , 
234-                 BubbleStyle . PADDING  +  ( BubbleStyle . LINE_HEIGHT  *  lineNumber )  + 
235-                     ( BubbleStyle . FONT_HEIGHT_RATIO  *  BubbleStyle . FONT_SIZE ) 
251+                 this . _style . padding , 
252+                 this . _style . padding  +  ( this . _style . lineHeight  *  lineNumber )  + 
253+                     ( this . _style . fontHeightRatio  *  this . _style . fontSize ) 
236254            ) ; 
237255        } 
238256
0 commit comments