OLD | NEW |
1 part of sprites; | 1 part of sprites; |
2 | 2 |
| 3 /// Options for setting up a [SpriteBox]. |
| 4 /// |
| 5 /// * [nativePoints], use the same points as the parent [Widget]. |
| 6 /// * [letterbox], use the size of the root node for the coordinate system, con
strain the aspect ratio and trim off |
| 7 /// areas that end up outside the screen. |
| 8 /// * [stretch], use the size of the root node for the coordinate system, scale
it to fit the size of the box. |
| 9 /// * [scaleToFit], similar to the letterbox option, but instead of trimming ar
eas the sprite system will be scaled |
| 10 /// down to fit the box. |
| 11 /// * [fixedWidth], uses the width of the root node to set the size of the coor
dinate system, this option will change |
| 12 /// the height of the root node to fit the box. |
| 13 /// * [fixedHeight], uses the height of the root node to set the size of the co
ordinate system, this option will change |
| 14 /// the width of the root node to fit the box. |
3 enum SpriteBoxTransformMode { | 15 enum SpriteBoxTransformMode { |
4 nativePoints, | 16 nativePoints, |
5 letterbox, | 17 letterbox, |
6 stretch, | 18 stretch, |
7 scaleToFit, | 19 scaleToFit, |
8 fixedWidth, | 20 fixedWidth, |
9 fixedHeight, | 21 fixedHeight, |
10 } | 22 } |
11 | 23 |
12 class SpriteBox extends RenderBox { | 24 class SpriteBox extends RenderBox { |
13 | 25 |
14 // Member variables | 26 // Member variables |
15 | 27 |
16 // Root node for drawing | 28 // Root node for drawing |
17 NodeWithSize _rootNode; | 29 NodeWithSize _rootNode; |
18 | 30 |
19 // Tracking of frame rate and updates | 31 // Tracking of frame rate and updates |
20 double _lastTimeStamp; | 32 double _lastTimeStamp; |
21 int _numFrames = 0; | 33 int _numFrames = 0; |
22 double _frameRate = 0.0; | 34 double _frameRate = 0.0; |
23 | 35 |
24 // Transformation mode | 36 // Transformation mode |
25 SpriteBoxTransformMode transformMode; | 37 SpriteBoxTransformMode _transformMode; |
26 // double _systemWidth; | 38 |
27 // double _systemHeight; | 39 /// The transform mode used by the [SpriteBox]. |
| 40 SpriteBoxTransformMode get transformMode => _transformMode; |
28 | 41 |
29 // Cached transformation matrix | 42 // Cached transformation matrix |
30 Matrix4 _transformMatrix; | 43 Matrix4 _transformMatrix; |
31 | 44 |
32 List<Node> _eventTargets; | 45 List<Node> _eventTargets; |
33 | 46 |
34 // Setup | 47 // Setup |
35 | 48 |
36 SpriteBox(NodeWithSize rootNode, [SpriteBoxTransformMode mode = SpriteBoxTrans
formMode.nativePoints]) { | 49 /// Creates a new SpriteBox with a node as its content, by default uses letter
boxing. |
| 50 /// |
| 51 /// The [rootNode] provides the content of the node tree, typically it's a cus
tom subclass of [NodeWithSize]. The |
| 52 /// [mode] provides different ways to scale the content to best fit it to the
screen. In most cases it's preferred to |
| 53 /// use a [SpriteWidget] that automatically wraps the SpriteBox. |
| 54 /// |
| 55 /// var spriteBox = new SpriteBox(myNode, SpriteBoxTransformMode.fixedHeig
ht); |
| 56 SpriteBox(NodeWithSize rootNode, [SpriteBoxTransformMode mode = SpriteBoxTrans
formMode.letterbox]) { |
37 assert(rootNode != null); | 57 assert(rootNode != null); |
38 assert(rootNode._spriteBox == null); | 58 assert(rootNode._spriteBox == null); |
39 | 59 |
40 // Setup root node | 60 // Setup root node |
41 _rootNode = rootNode; | 61 _rootNode = rootNode; |
42 | 62 |
43 // Assign SpriteBox reference to all the nodes | 63 // Assign SpriteBox reference to all the nodes |
44 _addSpriteBoxReference(_rootNode); | 64 _addSpriteBoxReference(_rootNode); |
45 | 65 |
46 // Setup transform mode | 66 // Setup transform mode |
47 transformMode = mode; | 67 _transformMode = mode; |
48 // _systemWidth = rootNode.size.width; | |
49 // _systemHeight = rootNode.size.height; | |
50 | 68 |
51 _scheduleTick(); | 69 _scheduleTick(); |
52 } | 70 } |
53 | 71 |
54 void _addSpriteBoxReference(Node node) { | 72 void _addSpriteBoxReference(Node node) { |
55 node._spriteBox = this; | 73 node._spriteBox = this; |
56 for (Node child in node._children) { | 74 for (Node child in node._children) { |
57 _addSpriteBoxReference(child); | 75 _addSpriteBoxReference(child); |
58 } | 76 } |
59 } | 77 } |
60 | 78 |
61 // Properties | 79 // Properties |
62 | 80 |
63 double get systemWidth => rootNode.size.width; | 81 /// The root node of the node tree that is rendered by this box. |
64 double get systemHeight => rootNode.size.height; | 82 /// |
65 | 83 /// var rootNode = mySpriteBox.rootNode; |
66 NodeWithSize get rootNode => _rootNode; | 84 NodeWithSize get rootNode => _rootNode; |
67 | 85 |
68 void performLayout() { | 86 void performLayout() { |
69 size = constraints.constrain(Size.infinite); | 87 size = constraints.constrain(Size.infinite); |
70 _invalidateTransformMatrix(); | 88 _invalidateTransformMatrix(); |
71 _callSpriteBoxPerformedLayout(_rootNode); | 89 _callSpriteBoxPerformedLayout(_rootNode); |
72 } | 90 } |
73 | 91 |
74 // Event handling | 92 // Event handling |
75 | 93 |
(...skipping 15 matching lines...) Expand all Loading... |
91 } | 109 } |
92 | 110 |
93 // Add children in front of this node | 111 // Add children in front of this node |
94 while (i < children.length) { | 112 while (i < children.length) { |
95 Node child = children[i]; | 113 Node child = children[i]; |
96 _addEventTargets(child, eventTargets); | 114 _addEventTargets(child, eventTargets); |
97 i++; | 115 i++; |
98 } | 116 } |
99 } | 117 } |
100 | 118 |
101 void handleEvent(Event event, SpriteBoxHitTestEntry entry) { | 119 void handleEvent(Event event, _SpriteBoxHitTestEntry entry) { |
102 if (event is PointerEvent) { | 120 if (event is PointerEvent) { |
103 | 121 |
104 if (event.type == 'pointerdown') { | 122 if (event.type == 'pointerdown') { |
105 // Build list of event targets | 123 // Build list of event targets |
106 if (_eventTargets == null) { | 124 if (_eventTargets == null) { |
107 _eventTargets = []; | 125 _eventTargets = []; |
108 _addEventTargets(_rootNode, _eventTargets); | 126 _addEventTargets(_rootNode, _eventTargets); |
109 } | 127 } |
110 | 128 |
111 // Find the once that are hit by the pointer | 129 // Find the once that are hit by the pointer |
(...skipping 29 matching lines...) Expand all Loading... |
141 // De-register pointer for nodes that doesn't handle multiple pointers | 159 // De-register pointer for nodes that doesn't handle multiple pointers |
142 for (Node node in targets) { | 160 for (Node node in targets) { |
143 if (event.type == 'pointerup' || event.type == 'pointercancel') { | 161 if (event.type == 'pointerup' || event.type == 'pointercancel') { |
144 node._handlingPointer = null; | 162 node._handlingPointer = null; |
145 } | 163 } |
146 } | 164 } |
147 } | 165 } |
148 } | 166 } |
149 | 167 |
150 bool hitTest(HitTestResult result, { Point position }) { | 168 bool hitTest(HitTestResult result, { Point position }) { |
151 result.add(new SpriteBoxHitTestEntry(this, position)); | 169 result.add(new _SpriteBoxHitTestEntry(this, position)); |
152 return true; | 170 return true; |
153 } | 171 } |
154 | 172 |
155 // Rendering | 173 // Rendering |
156 | 174 |
| 175 /// The transformation matrix used to transform the root node to the space of
the box. |
| 176 /// |
| 177 /// It's uncommon to need access to this property. |
| 178 /// |
| 179 /// var matrix = mySpriteBox.transformMatrix; |
157 Matrix4 get transformMatrix { | 180 Matrix4 get transformMatrix { |
158 // Get cached matrix if available | 181 // Get cached matrix if available |
159 if (_transformMatrix != null) { | 182 if (_transformMatrix != null) { |
160 return _transformMatrix; | 183 return _transformMatrix; |
161 } | 184 } |
162 | 185 |
163 _transformMatrix = new Matrix4.identity(); | 186 _transformMatrix = new Matrix4.identity(); |
164 | 187 |
165 // Calculate matrix | 188 // Calculate matrix |
166 double scaleX = 1.0; | 189 double scaleX = 1.0; |
167 double scaleY = 1.0; | 190 double scaleY = 1.0; |
168 double offsetX = 0.0; | 191 double offsetX = 0.0; |
169 double offsetY = 0.0; | 192 double offsetY = 0.0; |
170 | 193 |
171 double systemWidth = rootNode.size.width; | 194 double systemWidth = rootNode.size.width; |
172 double systemHeight = rootNode.size.height; | 195 double systemHeight = rootNode.size.height; |
173 | 196 |
174 switch(transformMode) { | 197 switch(_transformMode) { |
175 case SpriteBoxTransformMode.stretch: | 198 case SpriteBoxTransformMode.stretch: |
176 scaleX = size.width/systemWidth; | 199 scaleX = size.width/systemWidth; |
177 scaleY = size.height/systemHeight; | 200 scaleY = size.height/systemHeight; |
178 break; | 201 break; |
179 case SpriteBoxTransformMode.letterbox: | 202 case SpriteBoxTransformMode.letterbox: |
180 scaleX = size.width/systemWidth; | 203 scaleX = size.width/systemWidth; |
181 scaleY = size.height/systemHeight; | 204 scaleY = size.height/systemHeight; |
182 if (scaleX > scaleY) { | 205 if (scaleX > scaleY) { |
183 scaleY = scaleX; | 206 scaleY = scaleX; |
184 offsetY = (size.height - scaleY * systemHeight)/2.0; | 207 offsetY = (size.height - scaleY * systemHeight)/2.0; |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
280 | 303 |
281 void _callSpriteBoxPerformedLayout(Node node) { | 304 void _callSpriteBoxPerformedLayout(Node node) { |
282 node.spriteBoxPerformedLayout(); | 305 node.spriteBoxPerformedLayout(); |
283 for (Node child in node.children) { | 306 for (Node child in node.children) { |
284 _callSpriteBoxPerformedLayout(child); | 307 _callSpriteBoxPerformedLayout(child); |
285 } | 308 } |
286 } | 309 } |
287 | 310 |
288 // Hit tests | 311 // Hit tests |
289 | 312 |
| 313 /// Finds all nodes at a position defined in the box's coordinates. |
| 314 /// |
| 315 /// Use this method with caution. It searches the complete node tree to locate
the nodes, which can be slow if the |
| 316 /// node tree is large. |
| 317 /// |
| 318 /// List nodes = mySpriteBox.findNodesAtPosition(new Point(50.0, 50.0)); |
290 List<Node> findNodesAtPosition(Point position) { | 319 List<Node> findNodesAtPosition(Point position) { |
291 assert(position != null); | 320 assert(position != null); |
292 | 321 |
293 List<Node> nodes = []; | 322 List<Node> nodes = []; |
294 | 323 |
295 // Traverse the render tree and find objects at the position | 324 // Traverse the render tree and find objects at the position |
296 _addNodesAtPosition(_rootNode, position, nodes); | 325 _addNodesAtPosition(_rootNode, position, nodes); |
297 | 326 |
298 return nodes; | 327 return nodes; |
299 } | 328 } |
300 | 329 |
301 _addNodesAtPosition(Node node, Point position, List<Node> list) { | 330 _addNodesAtPosition(Node node, Point position, List<Node> list) { |
302 // Visit children first | 331 // Visit children first |
303 for (Node child in node.children) { | 332 for (Node child in node.children) { |
304 _addNodesAtPosition(child, position, list); | 333 _addNodesAtPosition(child, position, list); |
305 } | 334 } |
306 // Do the hit test | 335 // Do the hit test |
307 Point posInNodeSpace = node.convertPointToNodeSpace(position); | 336 Point posInNodeSpace = node.convertPointToNodeSpace(position); |
308 if (node.isPointInside(posInNodeSpace)) { | 337 if (node.isPointInside(posInNodeSpace)) { |
309 list.add(node); | 338 list.add(node); |
310 } | 339 } |
311 } | 340 } |
312 } | 341 } |
313 | 342 |
314 class SpriteBoxHitTestEntry extends BoxHitTestEntry { | 343 class _SpriteBoxHitTestEntry extends BoxHitTestEntry { |
315 List<Node> nodeTargets; | 344 List<Node> nodeTargets; |
316 SpriteBoxHitTestEntry(RenderBox target, Point localPosition) : super(target, l
ocalPosition); | 345 _SpriteBoxHitTestEntry(RenderBox target, Point localPosition) : super(target,
localPosition); |
317 } | 346 } |
318 | 347 |
| 348 /// An event that is passed down the node tree when pointer events occur. The Sp
riteBoxEvent is typically handled in |
| 349 /// the handleEvent method of [Node]. |
319 class SpriteBoxEvent { | 350 class SpriteBoxEvent { |
320 Point boxPosition; | |
321 String type; | |
322 int pointer; | |
323 | 351 |
| 352 /// The position of the event in box coordinates. |
| 353 /// |
| 354 /// You can use the convertPointToNodeSpace of [Node] to convert the position
to local coordinates. |
| 355 /// |
| 356 /// bool handleEvent(SpriteBoxEvent event) { |
| 357 /// Point localPosition = convertPointToNodeSpace(event.boxPosition); |
| 358 /// if (event.type == 'pointerdown') { |
| 359 /// // Do something! |
| 360 /// } |
| 361 /// } |
| 362 final Point boxPosition; |
| 363 |
| 364 /// The type of event, there are currently four valid types, 'pointerdown', 'p
ointermoved', 'pointerup', and |
| 365 /// 'pointercancel'. |
| 366 /// |
| 367 /// if (event.type == 'pointerdown') { |
| 368 /// // Do something! |
| 369 /// } |
| 370 final String type; |
| 371 |
| 372 /// The id of the pointer. Each pointer on the screen will have a unique point
er id. |
| 373 /// |
| 374 /// if (event.pointer == firstPointerId) { |
| 375 /// // Do something |
| 376 /// } |
| 377 final int pointer; |
| 378 |
| 379 /// Creates a new SpriteBoxEvent, typically this is done internally inside the
SpriteBox. |
| 380 /// |
| 381 /// var event = new SpriteBoxEvent(new Point(50.0, 50.0), 'pointerdown', 0
); |
324 SpriteBoxEvent(this.boxPosition, this.type, this.pointer); | 382 SpriteBoxEvent(this.boxPosition, this.type, this.pointer); |
325 } | 383 } |
OLD | NEW |