@@ -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 } px ${ BubbleStyle . FONT } , sans-serif` ;
134+ this . measurementProvider . clearCache ( ) ;
135+ this . _canvas . getContext ( '2d' ) . font = `${ this . _style . fontSize } px ${ this . _style . font } , sans-serif` ;
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 } px ${ BubbleStyle . FONT } , sans-serif` ;
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