@@ -3,25 +3,23 @@ const twgl = require('twgl.js');
3
3
const CanvasMeasurementProvider = require ( './util/canvas-measurement-provider' ) ;
4
4
const Skin = require ( './Skin' ) ;
5
5
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'
25
23
} ;
26
24
27
25
const MAX_SCALE = 10 ;
@@ -64,6 +62,13 @@ class TextBubbleSkin extends Skin {
64
62
/** @type {boolean } */
65
63
this . _textureDirty = true ;
66
64
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
+
67
72
this . measurementProvider = new CanvasMeasurementProvider ( this . _canvas . getContext ( '2d' ) ) ;
68
73
this . textWrapper = renderer . createTextWrapper ( this . measurementProvider ) ;
69
74
@@ -108,18 +113,33 @@ class TextBubbleSkin extends Skin {
108
113
this . emitWasAltered ( ) ;
109
114
}
110
115
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
+
111
130
/**
112
131
* Re-style the canvas after resizing it. This is necessary to ensure proper text measurement.
113
132
*/
114
133
_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` ;
116
136
}
117
137
118
138
/**
119
139
* Update the array of wrapped lines and the text dimensions.
120
140
*/
121
141
_reflowLines ( ) {
122
- this . _lines = this . textWrapper . wrapText ( BubbleStyle . MAX_LINE_WIDTH , this . _text ) ;
142
+ this . _lines = this . textWrapper . wrapText ( this . _style . maxLineWidth , this . _text ) ;
123
143
124
144
// Measure width of longest line to avoid extra-wide bubbles
125
145
let longestLineWidth = 0 ;
@@ -128,14 +148,14 @@ class TextBubbleSkin extends Skin {
128
148
}
129
149
130
150
// 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 ) ;
133
153
134
154
this . _textAreaSize . width = paddedWidth ;
135
155
this . _textAreaSize . height = paddedHeight ;
136
156
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 ;
139
159
140
160
this . _textDirty = false ;
141
161
}
@@ -158,14 +178,13 @@ class TextBubbleSkin extends Skin {
158
178
// Resize the canvas to the correct screen-space size
159
179
this . _canvas . width = Math . ceil ( this . _size [ 0 ] * scale ) ;
160
180
this . _canvas . height = Math . ceil ( this . _size [ 1 ] * scale ) ;
161
- this . _restyleCanvas ( ) ;
162
181
163
182
// Reset the transform before clearing to ensure 100% clearage
164
183
ctx . setTransform ( 1 , 0 , 0 , 1 , 0 , 0 ) ;
165
184
ctx . clearRect ( 0 , 0 , this . _canvas . width , this . _canvas . height ) ;
166
185
167
186
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 ) ;
169
188
170
189
// If the text bubble points leftward, flip the canvas
171
190
ctx . save ( ) ;
@@ -176,16 +195,16 @@ class TextBubbleSkin extends Skin {
176
195
177
196
// Draw the bubble's rounded borders
178
197
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 ) ;
185
204
186
205
// Translate the canvas so we don't have to do a bunch of width/height arithmetic
187
206
ctx . save ( ) ;
188
- ctx . translate ( paddedWidth - BubbleStyle . CORNER_RADIUS , paddedHeight ) ;
207
+ ctx . translate ( paddedWidth - this . _style . cornerRadius , paddedHeight ) ;
189
208
190
209
// Draw the bubble's "tail"
191
210
if ( this . _bubbleType === 'say' ) {
@@ -212,9 +231,9 @@ class TextBubbleSkin extends Skin {
212
231
// Un-translate the canvas and fill + stroke the text bubble
213
232
ctx . restore ( ) ;
214
233
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 ;
218
237
219
238
ctx . stroke ( ) ;
220
239
ctx . fill ( ) ;
@@ -223,16 +242,15 @@ class TextBubbleSkin extends Skin {
223
242
ctx . restore ( ) ;
224
243
225
244
// 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 ;
228
246
const lines = this . _lines ;
229
247
for ( let lineNumber = 0 ; lineNumber < lines . length ; lineNumber ++ ) {
230
248
const line = lines [ lineNumber ] ;
231
249
ctx . fillText (
232
250
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 )
236
254
) ;
237
255
}
238
256
0 commit comments