OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of swarmlib; | 5 part of swarmlib; |
6 | 6 |
7 // This file contains View framework classes. | 7 // This file contains View framework classes. |
8 // As it grows, it may need to be split into multiple files. | 8 // As it grows, it may need to be split into multiple files. |
9 | 9 |
10 /** A factory that creates a view from a data model. */ | 10 /** A factory that creates a view from a data model. */ |
(...skipping 13 matching lines...) Expand all Loading... |
24 /** The width of the created view for a specific data model. */ | 24 /** The width of the created view for a specific data model. */ |
25 int getWidth(D item); | 25 int getWidth(D item); |
26 | 26 |
27 /** The height of the created view for a specific data model. */ | 27 /** The height of the created view for a specific data model. */ |
28 int getHeight(D item); | 28 int getHeight(D item); |
29 } | 29 } |
30 | 30 |
31 /** A collection of event listeners. */ | 31 /** A collection of event listeners. */ |
32 class EventListeners { | 32 class EventListeners { |
33 var listeners; | 33 var listeners; |
34 EventListeners() { | 34 EventListeners() { |
35 listeners = new List(); | 35 listeners = new List(); |
36 } | 36 } |
37 | 37 |
38 void addListener(listener) { | 38 void addListener(listener) { |
39 listeners.add(listener); | 39 listeners.add(listener); |
40 } | 40 } |
41 | 41 |
42 void fire(var event) { | 42 void fire(var event) { |
43 for (final listener in listeners) { | 43 for (final listener in listeners) { |
44 listener(event); | 44 listener(event); |
45 } | 45 } |
46 } | 46 } |
47 } | 47 } |
48 | 48 |
49 | |
50 /** | 49 /** |
51 * Private view class used to store placeholder views for detatched ListView | 50 * Private view class used to store placeholder views for detatched ListView |
52 * elements. | 51 * elements. |
53 */ | 52 */ |
54 class _PlaceholderView extends View { | 53 class _PlaceholderView extends View { |
55 _PlaceholderView() : super() {} | 54 _PlaceholderView() : super() {} |
56 | 55 |
57 Element render() => new Element.tag('div'); | 56 Element render() => new Element.tag('div'); |
58 } | 57 } |
59 | 58 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
91 | 90 |
92 int getEstimatedLength(int viewLength); | 91 int getEstimatedLength(int viewLength); |
93 /** | 92 /** |
94 * Snap a specified index to the nearest visible view given the [viewLength]. | 93 * Snap a specified index to the nearest visible view given the [viewLength]. |
95 */ | 94 */ |
96 int getSnapIndex(num offset, num viewLength); | 95 int getSnapIndex(num offset, num viewLength); |
97 /** | 96 /** |
98 * Returns an interval specifying what views are currently visible given a | 97 * Returns an interval specifying what views are currently visible given a |
99 * particular [:offset:]. | 98 * particular [:offset:]. |
100 */ | 99 */ |
101 Interval computeVisibleInterval(num offset, num viewLength, | 100 Interval computeVisibleInterval(num offset, num viewLength, num bufferLength); |
102 num bufferLength); | |
103 } | 101 } |
104 | 102 |
105 /** | 103 /** |
106 * Base class used for the simple fixed size item [:ListView:] classes and more | 104 * Base class used for the simple fixed size item [:ListView:] classes and more |
107 * complex list view classes such as [:VariableSizeListView:] using a | 105 * complex list view classes such as [:VariableSizeListView:] using a |
108 * [:ListViewLayout:] class to drive the actual layout. | 106 * [:ListViewLayout:] class to drive the actual layout. |
109 */ | 107 */ |
110 class GenericListView<D> extends View { | 108 class GenericListView<D> extends View { |
111 /** Minimum throw distance in pixels to trigger snapping to the next item. */ | 109 /** Minimum throw distance in pixels to trigger snapping to the next item. */ |
112 static const SNAP_TO_NEXT_THROW_THRESHOLD = 15; | 110 static const SNAP_TO_NEXT_THROW_THRESHOLD = 15; |
(...skipping 18 matching lines...) Expand all Loading... |
131 ListViewLayout<D> _layout; | 129 ListViewLayout<D> _layout; |
132 D _lastSelectedItem; | 130 D _lastSelectedItem; |
133 PageState _pages; | 131 PageState _pages; |
134 | 132 |
135 /** | 133 /** |
136 * Creates a new GenericListView with the given layout and data. If [:_data:] | 134 * Creates a new GenericListView with the given layout and data. If [:_data:] |
137 * is an [:ObservableList<T>:] then it will listen to changes to the list | 135 * is an [:ObservableList<T>:] then it will listen to changes to the list |
138 * and update the view appropriately. | 136 * and update the view appropriately. |
139 */ | 137 */ |
140 GenericListView( | 138 GenericListView( |
141 this._layout, | 139 this._layout, |
142 this._data, | 140 this._data, |
143 this._scrollable, | 141 this._scrollable, |
144 this._vertical, | 142 this._vertical, |
145 this._selectedItem, | 143 this._selectedItem, |
146 this._snapToItems, | 144 this._snapToItems, |
147 this._paginate, | 145 this._paginate, |
148 this._removeClippedViews, | 146 this._removeClippedViews, |
149 this._showScrollbar, | 147 this._showScrollbar, |
150 this._pages) | 148 this._pages) |
151 : super(), | 149 : super(), |
152 _activeInterval = new Interval(0, 0), | 150 _activeInterval = new Interval(0, 0), |
153 _itemViews = new Map<int, View>() { | 151 _itemViews = new Map<int, View>() { |
154 // TODO(rnystrom): Move this into enterDocument once we have an exitDocument | 152 // TODO(rnystrom): Move this into enterDocument once we have an exitDocument |
155 // that we can use to unregister it. | 153 // that we can use to unregister it. |
156 if (_scrollable) { | 154 if (_scrollable) { |
157 window.onResize.listen((Event event) { | 155 window.onResize.listen((Event event) { |
158 if (isInDocument) { | 156 if (isInDocument) { |
159 onResize(); | 157 onResize(); |
160 } | 158 } |
161 }); | 159 }); |
162 } | 160 } |
163 } | 161 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 node.nodes.add(_containerElem); | 207 node.nodes.add(_containerElem); |
210 } else { | 208 } else { |
211 _containerElem = node; | 209 _containerElem = node; |
212 } | 210 } |
213 | 211 |
214 if (_scrollable) { | 212 if (_scrollable) { |
215 scroller = new Scroller( | 213 scroller = new Scroller( |
216 _containerElem, | 214 _containerElem, |
217 _vertical /* verticalScrollEnabled */, | 215 _vertical /* verticalScrollEnabled */, |
218 !_vertical /* horizontalScrollEnabled */, | 216 !_vertical /* horizontalScrollEnabled */, |
219 true /* momentumEnabled */, | 217 true /* momentumEnabled */, () { |
220 () { | 218 num width = _layout.getWidth(_viewLength); |
221 num width = _layout.getWidth(_viewLength); | 219 num height = _layout.getHeight(_viewLength); |
222 num height = _layout.getHeight(_viewLength); | 220 width = width != null ? width : 0; |
223 width = width != null ? width : 0; | 221 height = height != null ? height : 0; |
224 height = height != null ? height : 0; | 222 return new Size(width, height); |
225 return new Size(width, height); | 223 }, |
226 }, | 224 _paginate && _snapToItems |
227 _paginate && _snapToItems ? | 225 ? Scroller.FAST_SNAP_DECELERATION_FACTOR |
228 Scroller.FAST_SNAP_DECELERATION_FACTOR : 1); | 226 : 1); |
229 scroller.onContentMoved.listen((e) => renderVisibleItems(false)); | 227 scroller.onContentMoved.listen((e) => renderVisibleItems(false)); |
230 if (_pages != null) { | 228 if (_pages != null) { |
231 watch(_pages.target, (s) => _onPageSelected()); | 229 watch(_pages.target, (s) => _onPageSelected()); |
232 } | 230 } |
233 | 231 |
234 if (_snapToItems) { | 232 if (_snapToItems) { |
235 scroller.onDecelStart.listen((e) => _decelStart()); | 233 scroller.onDecelStart.listen((e) => _decelStart()); |
236 scroller.onScrollerDragEnd.listen((e) => _decelStart()); | 234 scroller.onScrollerDragEnd.listen((e) => _decelStart()); |
237 } | 235 } |
238 if (_showScrollbar) { | 236 if (_showScrollbar) { |
239 _scrollbar = new Scrollbar(scroller, true); | 237 _scrollbar = new Scrollbar(scroller, true); |
240 } | 238 } |
241 } else { | 239 } else { |
242 _reserveArea(); | 240 _reserveArea(); |
243 renderVisibleItems(true); | 241 renderVisibleItems(true); |
244 } | 242 } |
245 | 243 |
246 return node; | 244 return node; |
247 } | 245 } |
248 | 246 |
249 void afterRender(Element node) { | 247 void afterRender(Element node) { |
250 // If our data source is observable, observe it. | 248 // If our data source is observable, observe it. |
251 if (_data is ObservableList<D>) { | 249 if (_data is ObservableList<D>) { |
252 ObservableList<D> observable = _data; | 250 ObservableList<D> observable = _data; |
253 attachWatch(observable, (EventSummary e) { | 251 attachWatch(observable, (EventSummary e) { |
254 if (e.target == observable) { | 252 if (e.target == observable) { |
255 onDataChange(); | 253 onDataChange(); |
256 } | 254 } |
257 }); | 255 }); |
258 } | 256 } |
259 | 257 |
260 if (_selectedItem != null) { | 258 if (_selectedItem != null) { |
261 addOnClick((Event e) { _onClick(e); }); | 259 addOnClick((Event e) { |
| 260 _onClick(e); |
| 261 }); |
262 } | 262 } |
263 | 263 |
264 if (_selectedItem != null) { | 264 if (_selectedItem != null) { |
265 watch(_selectedItem, (EventSummary summary) => onSelectedItemChange()); | 265 watch(_selectedItem, (EventSummary summary) => onSelectedItemChange()); |
266 } | 266 } |
267 } | 267 } |
268 | 268 |
269 void onDataChange() { | 269 void onDataChange() { |
270 _layout.onDataChange(); | 270 _layout.onDataChange(); |
271 _renderItems(); | 271 _renderItems(); |
272 } | 272 } |
273 | 273 |
274 void _reserveArea() { | 274 void _reserveArea() { |
275 final style = _containerElem.style; | 275 final style = _containerElem.style; |
276 int width = _layout.getWidth(_viewLength); | 276 int width = _layout.getWidth(_viewLength); |
277 int height = _layout.getHeight(_viewLength); | 277 int height = _layout.getHeight(_viewLength); |
278 if (width != null) { | 278 if (width != null) { |
279 style.width = '${width}px'; | 279 style.width = '${width}px'; |
280 } | 280 } |
281 if (height != null) { | 281 if (height != null) { |
282 style.height = '${height}px'; | 282 style.height = '${height}px'; |
283 } | 283 } |
284 // TODO(jacobr): this should be specified by the default CSS for a | 284 // TODO(jacobr): this should be specified by the default CSS for a |
285 // GenericListView. | 285 // GenericListView. |
286 style.overflow = 'hidden'; | 286 style.overflow = 'hidden'; |
287 } | 287 } |
288 | 288 |
289 | |
290 void onResize() { | 289 void onResize() { |
291 int lastViewLength = _viewLength; | 290 int lastViewLength = _viewLength; |
292 scheduleMicrotask(() { | 291 scheduleMicrotask(() { |
293 _viewLength = _vertical ? node.offset.height : node.offset.width; | 292 _viewLength = _vertical ? node.offset.height : node.offset.width; |
294 if (_viewLength != lastViewLength) { | 293 if (_viewLength != lastViewLength) { |
295 if (_scrollbar != null) { | 294 if (_scrollbar != null) { |
296 _scrollbar.refresh(); | 295 _scrollbar.refresh(); |
297 } | 296 } |
298 renderVisibleItems(true); | 297 renderVisibleItems(true); |
299 } | 298 } |
(...skipping 15 matching lines...) Expand all Loading... |
315 if (_paginate) { | 314 if (_paginate) { |
316 int newPage = Math.max(0, _layout.getPage(index, _viewLength) + delta); | 315 int newPage = Math.max(0, _layout.getPage(index, _viewLength) + delta); |
317 index = _layout.getPageStartIndex(newPage, _viewLength); | 316 index = _layout.getPageStartIndex(newPage, _viewLength); |
318 } else { | 317 } else { |
319 index += delta; | 318 index += delta; |
320 } | 319 } |
321 return GoogleMath.clamp(index, 0, _data.length - 1); | 320 return GoogleMath.clamp(index, 0, _data.length - 1); |
322 } | 321 } |
323 | 322 |
324 void _decelStart() { | 323 void _decelStart() { |
325 num currentTarget = scroller.verticalEnabled ? | 324 num currentTarget = scroller.verticalEnabled |
326 scroller.currentTarget.y : scroller.currentTarget.x; | 325 ? scroller.currentTarget.y |
327 num current = scroller.verticalEnabled ? | 326 : scroller.currentTarget.x; |
328 scroller.contentOffset.y : scroller.contentOffset.x; | 327 num current = scroller.verticalEnabled |
| 328 ? scroller.contentOffset.y |
| 329 : scroller.contentOffset.x; |
329 num targetIndex = _layout.getSnapIndex(currentTarget, _viewLength); | 330 num targetIndex = _layout.getSnapIndex(currentTarget, _viewLength); |
330 if (current != currentTarget) { | 331 if (current != currentTarget) { |
331 // The user is throwing rather than statically releasing. | 332 // The user is throwing rather than statically releasing. |
332 // For this case, we want to move them to the next snap interval | 333 // For this case, we want to move them to the next snap interval |
333 // as long as they made at least a minimal throw gesture. | 334 // as long as they made at least a minimal throw gesture. |
334 num currentIndex = _layout.getSnapIndex(current, _viewLength); | 335 num currentIndex = _layout.getSnapIndex(current, _viewLength); |
335 if (currentIndex == targetIndex && | 336 if (currentIndex == targetIndex && |
336 (currentTarget - current).abs() > SNAP_TO_NEXT_THROW_THRESHOLD && | 337 (currentTarget - current).abs() > SNAP_TO_NEXT_THROW_THRESHOLD && |
337 -_layout.getOffset(targetIndex) != currentTarget) { | 338 -_layout.getOffset(targetIndex) != currentTarget) { |
338 num snappedCurrentPosition = -_layout.getOffset(targetIndex); | 339 num snappedCurrentPosition = -_layout.getOffset(targetIndex); |
339 targetIndex = getNextIndex(targetIndex, currentTarget < current); | 340 targetIndex = getNextIndex(targetIndex, currentTarget < current); |
340 } | 341 } |
341 } | 342 } |
342 num targetPosition = -_layout.getOffset(targetIndex); | 343 num targetPosition = -_layout.getOffset(targetIndex); |
343 if (currentTarget != targetPosition) { | 344 if (currentTarget != targetPosition) { |
344 if (scroller.verticalEnabled) { | 345 if (scroller.verticalEnabled) { |
345 scroller.throwTo(scroller.contentOffset.x, targetPosition); | 346 scroller.throwTo(scroller.contentOffset.x, targetPosition); |
346 } else { | 347 } else { |
347 scroller.throwTo(targetPosition, scroller.contentOffset.y); | 348 scroller.throwTo(targetPosition, scroller.contentOffset.y); |
348 } | 349 } |
349 } else { | 350 } else { |
350 // Update the target page only after we are all done animating. | 351 // Update the target page only after we are all done animating. |
351 if (_pages != null) { | 352 if (_pages != null) { |
352 _pages.target.value =_layout.getPage(targetIndex, _viewLength); | 353 _pages.target.value = _layout.getPage(targetIndex, _viewLength); |
353 } | 354 } |
354 } | 355 } |
355 } | 356 } |
356 | 357 |
357 void _renderItems() { | 358 void _renderItems() { |
358 for (int i = _activeInterval.start; i < _activeInterval.end; i++) { | 359 for (int i = _activeInterval.start; i < _activeInterval.end; i++) { |
359 _removeView(i); | 360 _removeView(i); |
360 } | 361 } |
361 _itemViews.clear(); | 362 _itemViews.clear(); |
362 _activeInterval = new Interval(0, 0); | 363 _activeInterval = new Interval(0, 0); |
363 if (scroller == null) { | 364 if (scroller == null) { |
364 _reserveArea(); | 365 _reserveArea(); |
365 } | 366 } |
366 renderVisibleItems(false); | 367 renderVisibleItems(false); |
367 } | 368 } |
368 | 369 |
369 void _onPageSelected() { | 370 void _onPageSelected() { |
370 if (_pages.target != | 371 if (_pages.target != _layout.getPage(_activeInterval.start, _viewLength)) { |
371 _layout.getPage(_activeInterval.start, _viewLength)) { | |
372 _throwTo(_layout.getOffset( | 372 _throwTo(_layout.getOffset( |
373 _layout.getPageStartIndex(_pages.target.value, _viewLength))); | 373 _layout.getPageStartIndex(_pages.target.value, _viewLength))); |
374 } | 374 } |
375 } | 375 } |
376 | 376 |
377 num get _offset { | 377 num get _offset { |
378 return scroller.verticalEnabled ? | 378 return scroller.verticalEnabled |
379 scroller.getVerticalOffset() : scroller.getHorizontalOffset(); | 379 ? scroller.getVerticalOffset() |
| 380 : scroller.getHorizontalOffset(); |
380 } | 381 } |
381 | 382 |
382 /** | 383 /** |
383 * Calculates visible interval, based on the scroller position. | 384 * Calculates visible interval, based on the scroller position. |
384 */ | 385 */ |
385 Interval getVisibleInterval() { | 386 Interval getVisibleInterval() { |
386 return _layout.computeVisibleInterval(_offset, _viewLength, 0); | 387 return _layout.computeVisibleInterval(_offset, _viewLength, 0); |
387 } | 388 } |
388 | 389 |
389 void renderVisibleItems(bool lengthChanged) { | 390 void renderVisibleItems(bool lengthChanged) { |
390 Interval targetInterval; | 391 Interval targetInterval; |
391 if (scroller != null) { | 392 if (scroller != null) { |
392 targetInterval = getVisibleInterval(); | 393 targetInterval = getVisibleInterval(); |
393 } else { | 394 } else { |
394 // If the view is not scrollable, render all elements. | 395 // If the view is not scrollable, render all elements. |
395 targetInterval = new Interval(0, _data.length); | 396 targetInterval = new Interval(0, _data.length); |
396 } | 397 } |
397 | 398 |
398 if (_pages != null) { | 399 if (_pages != null) { |
399 _pages.current.value = | 400 _pages.current.value = _layout.getPage(targetInterval.start, _viewLength); |
400 _layout.getPage(targetInterval.start, _viewLength); | |
401 } | 401 } |
402 if (_pages != null) { | 402 if (_pages != null) { |
403 _pages.length.value = _data.length > 0 ? | 403 _pages.length.value = _data.length > 0 |
404 _layout.getPage(_data.length - 1, _viewLength) + 1 : 0; | 404 ? _layout.getPage(_data.length - 1, _viewLength) + 1 |
| 405 : 0; |
405 } | 406 } |
406 | 407 |
407 if (!_removeClippedViews) { | 408 if (!_removeClippedViews) { |
408 // Avoid removing clipped views by extending the target interval to | 409 // Avoid removing clipped views by extending the target interval to |
409 // include the existing interval of rendered views. | 410 // include the existing interval of rendered views. |
410 targetInterval = targetInterval.union(_activeInterval); | 411 targetInterval = targetInterval.union(_activeInterval); |
411 } | 412 } |
412 | 413 |
413 if (lengthChanged == false && targetInterval == _activeInterval) { | 414 if (lengthChanged == false && targetInterval == _activeInterval) { |
414 return; | 415 return; |
415 } | 416 } |
416 | 417 |
417 // TODO(jacobr): add unittests that this code behaves correctly. | 418 // TODO(jacobr): add unittests that this code behaves correctly. |
418 | 419 |
419 // Remove views that are not needed anymore | 420 // Remove views that are not needed anymore |
420 for (int i = _activeInterval.start, | 421 for (int i = _activeInterval.start, |
421 end = Math.min(targetInterval.start, _activeInterval.end); | 422 end = Math.min(targetInterval.start, _activeInterval.end); |
422 i < end; i++) { | 423 i < end; |
| 424 i++) { |
423 _removeView(i); | 425 _removeView(i); |
424 } | 426 } |
425 for (int i = Math.max(targetInterval.end, _activeInterval.start); | 427 for (int i = Math.max(targetInterval.end, _activeInterval.start); |
426 i < _activeInterval.end; i++) { | 428 i < _activeInterval.end; |
| 429 i++) { |
427 _removeView(i); | 430 _removeView(i); |
428 } | 431 } |
429 | 432 |
430 // Add new views | 433 // Add new views |
431 for (int i = targetInterval.start, | 434 for (int i = targetInterval.start, |
432 end = Math.min(_activeInterval.start, targetInterval.end); | 435 end = Math.min(_activeInterval.start, targetInterval.end); |
433 i < end; i++) { | 436 i < end; |
| 437 i++) { |
434 _addView(i); | 438 _addView(i); |
435 } | 439 } |
436 for (int i = Math.max(_activeInterval.end, targetInterval.start); | 440 for (int i = Math.max(_activeInterval.end, targetInterval.start); |
437 i < targetInterval.end; i++) { | 441 i < targetInterval.end; |
| 442 i++) { |
438 _addView(i); | 443 _addView(i); |
439 } | 444 } |
440 | 445 |
441 _activeInterval = targetInterval; | 446 _activeInterval = targetInterval; |
442 } | 447 } |
443 | 448 |
444 void _removeView(int index) { | 449 void _removeView(int index) { |
445 // Do not remove placeholder views as they need to stay present in case | 450 // Do not remove placeholder views as they need to stay present in case |
446 // they scroll out of view and then back into view. | 451 // they scroll out of view and then back into view. |
447 if (!(_itemViews[index] is _PlaceholderView)) { | 452 if (!(_itemViews[index] is _PlaceholderView)) { |
448 // Remove from the active DOM but don't destroy. | 453 // Remove from the active DOM but don't destroy. |
449 _itemViews[index].node.remove(); | 454 _itemViews[index].node.remove(); |
450 childViewRemoved(_itemViews[index]); | 455 childViewRemoved(_itemViews[index]); |
451 } | 456 } |
452 } | 457 } |
453 | 458 |
454 View _newView(int index) { | 459 View _newView(int index) { |
455 final view = _layout.newView(index); | 460 final view = _layout.newView(index); |
456 view.node.attributes[INDEX_DATA_ATTRIBUTE] = index.toString(); | 461 view.node.attributes[INDEX_DATA_ATTRIBUTE] = index.toString(); |
457 return view; | 462 return view; |
458 } | 463 } |
459 | 464 |
460 View _addView(int index) { | 465 View _addView(int index) { |
461 if (_itemViews.containsKey(index)) { | 466 if (_itemViews.containsKey(index)) { |
462 final view = _itemViews[index]; | 467 final view = _itemViews[index]; |
463 _addViewHelper(view, index); | 468 _addViewHelper(view, index); |
464 childViewAdded(view); | 469 childViewAdded(view); |
465 return view; | 470 return view; |
466 } | 471 } |
467 | 472 |
468 final view = _newView(index); | 473 final view = _newView(index); |
469 _itemViews[index] = view; | 474 _itemViews[index] = view; |
470 // TODO(jacobr): its ugly to put this here... but its needed | 475 // TODO(jacobr): its ugly to put this here... but its needed |
471 // as typical even-odd css queries won't work as we only display some | 476 // as typical even-odd css queries won't work as we only display some |
472 // children at a time. | 477 // children at a time. |
473 if (index == 0) { | 478 if (index == 0) { |
474 view.addClass('first-child'); | 479 view.addClass('first-child'); |
475 } | 480 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
516 */ | 521 */ |
517 void reattachSubview(D data, View view, bool animate) { | 522 void reattachSubview(D data, View view, bool animate) { |
518 int index = findIndex(data); | 523 int index = findIndex(data); |
519 // TODO(jacobr): perform some validation that the view is | 524 // TODO(jacobr): perform some validation that the view is |
520 // really detached. | 525 // really detached. |
521 var currentPosition; | 526 var currentPosition; |
522 if (animate) { | 527 if (animate) { |
523 currentPosition = | 528 currentPosition = |
524 FxUtil.computeRelativePosition(view.node, _containerElem); | 529 FxUtil.computeRelativePosition(view.node, _containerElem); |
525 } | 530 } |
526 assert (_itemViews[index] is _PlaceholderView); | 531 assert(_itemViews[index] is _PlaceholderView); |
527 view.enterDocument(); | 532 view.enterDocument(); |
528 _itemViews[index].node.replaceWith(view.node); | 533 _itemViews[index].node.replaceWith(view.node); |
529 _itemViews[index] = view; | 534 _itemViews[index] = view; |
530 if (animate) { | 535 if (animate) { |
531 FxUtil.setTranslate(view.node, currentPosition.x, currentPosition.y, 0); | 536 FxUtil.setTranslate(view.node, currentPosition.x, currentPosition.y, 0); |
532 // The view's position is unchanged except now re-parented to | 537 // The view's position is unchanged except now re-parented to |
533 // the list view. | 538 // the list view. |
534 Timer.run(() { _positionSubview(view.node, index); }); | 539 Timer.run(() { |
| 540 _positionSubview(view.node, index); |
| 541 }); |
535 } else { | 542 } else { |
536 _positionSubview(view.node, index); | 543 _positionSubview(view.node, index); |
537 } | 544 } |
538 } | 545 } |
539 | 546 |
540 int findIndex(D targetItem) { | 547 int findIndex(D targetItem) { |
541 // TODO(jacobr): move this to a util library or modify this class so that | 548 // TODO(jacobr): move this to a util library or modify this class so that |
542 // the data is an List not a Collection. | 549 // the data is an List not a Collection. |
543 int i = 0; | 550 int i = 0; |
544 for (D item in _data) { | 551 for (D item in _data) { |
545 if (item == targetItem) { | 552 if (item == targetItem) { |
546 return i; | 553 return i; |
547 } | 554 } |
548 i++; | 555 i++; |
549 } | 556 } |
550 return null; | 557 return null; |
551 } | 558 } |
552 | 559 |
553 void _positionSubview(Element node, int index) { | 560 void _positionSubview(Element node, int index) { |
554 if (_vertical) { | 561 if (_vertical) { |
555 FxUtil.setTranslate(node, 0, _layout.getOffset(index), 0); | 562 FxUtil.setTranslate(node, 0, _layout.getOffset(index), 0); |
556 } else { | 563 } else { |
557 FxUtil.setTranslate(node, _layout.getOffset(index), 0, 0); | 564 FxUtil.setTranslate(node, _layout.getOffset(index), 0, 0); |
558 } | 565 } |
559 node.style.zIndex = index.toString(); | 566 node.style.zIndex = index.toString(); |
560 } | 567 } |
561 | 568 |
562 void _select(int index, bool selected) { | 569 void _select(int index, bool selected) { |
563 if (index != null) { | 570 if (index != null) { |
564 final subview = getSubview(index); | 571 final subview = getSubview(index); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
602 } | 609 } |
603 } | 610 } |
604 } | 611 } |
605 | 612 |
606 class FixedSizeListViewLayout<D> implements ListViewLayout<D> { | 613 class FixedSizeListViewLayout<D> implements ListViewLayout<D> { |
607 final ViewFactory<D> itemViewFactory; | 614 final ViewFactory<D> itemViewFactory; |
608 final bool _vertical; | 615 final bool _vertical; |
609 List<D> _data; | 616 List<D> _data; |
610 bool _paginate; | 617 bool _paginate; |
611 | 618 |
612 FixedSizeListViewLayout(this.itemViewFactory, this._data, this._vertical, | 619 FixedSizeListViewLayout( |
613 this._paginate); | 620 this.itemViewFactory, this._data, this._vertical, this._paginate); |
614 | 621 |
615 void onDataChange() {} | 622 void onDataChange() {} |
616 | 623 |
617 View newView(int index) { | 624 View newView(int index) { |
618 return itemViewFactory.newView(_data[index]); | 625 return itemViewFactory.newView(_data[index]); |
619 } | 626 } |
620 | 627 |
621 int get _itemLength { | 628 int get _itemLength { |
622 return _vertical ? itemViewFactory.height : itemViewFactory.width; | 629 return _vertical ? itemViewFactory.height : itemViewFactory.width; |
623 } | 630 } |
624 | 631 |
625 | |
626 int getWidth(int viewLength) { | 632 int getWidth(int viewLength) { |
627 return _vertical ? itemViewFactory.width : getLength(viewLength); | 633 return _vertical ? itemViewFactory.width : getLength(viewLength); |
628 } | 634 } |
629 | 635 |
630 int getHeight(int viewLength) { | 636 int getHeight(int viewLength) { |
631 return _vertical ? getLength(viewLength) : itemViewFactory.height; | 637 return _vertical ? getLength(viewLength) : itemViewFactory.height; |
632 } | 638 } |
633 | 639 |
634 int getEstimatedHeight(int viewLength) { | 640 int getEstimatedHeight(int viewLength) { |
635 // Returns the exact height as it is trivial to compute for this layout. | 641 // Returns the exact height as it is trivial to compute for this layout. |
636 return getHeight(viewLength); | 642 return getHeight(viewLength); |
637 } | 643 } |
638 | 644 |
639 int getEstimatedWidth(int viewLength) { | 645 int getEstimatedWidth(int viewLength) { |
640 // Returns the exact height as it is trivial to compute for this layout. | 646 // Returns the exact height as it is trivial to compute for this layout. |
641 return getWidth(viewLength); | 647 return getWidth(viewLength); |
642 } | 648 } |
643 | 649 |
644 int getEstimatedLength(int viewLength) { | 650 int getEstimatedLength(int viewLength) { |
645 // Returns the exact length as it is trivial to compute for this layout. | 651 // Returns the exact length as it is trivial to compute for this layout. |
646 return getLength(viewLength); | 652 return getLength(viewLength); |
647 } | 653 } |
648 | 654 |
649 int getLength(int viewLength) { | 655 int getLength(int viewLength) { |
650 int itemLength = | 656 int itemLength = _vertical ? itemViewFactory.height : itemViewFactory.width; |
651 _vertical ? itemViewFactory.height : itemViewFactory.width; | |
652 if (viewLength == null || viewLength == 0) { | 657 if (viewLength == null || viewLength == 0) { |
653 return itemLength * _data.length; | 658 return itemLength * _data.length; |
654 } else if (_paginate) { | 659 } else if (_paginate) { |
655 if (_data.length > 0) { | 660 if (_data.length > 0) { |
656 final pageLength = getPageLength(viewLength); | 661 final pageLength = getPageLength(viewLength); |
657 return getPage(_data.length - 1, viewLength) | 662 return getPage(_data.length - 1, viewLength) * pageLength + |
658 * pageLength + Math.max(viewLength, pageLength); | 663 Math.max(viewLength, pageLength); |
659 } else { | 664 } else { |
660 return 0; | 665 return 0; |
661 } | 666 } |
662 } else { | 667 } else { |
663 return itemLength * (_data.length - 1) + Math.max(viewLength, itemLength); | 668 return itemLength * (_data.length - 1) + Math.max(viewLength, itemLength); |
664 } | 669 } |
665 } | 670 } |
666 | 671 |
667 int getOffset(int index) { | 672 int getOffset(int index) { |
668 return index * _itemLength; | 673 return index * _itemLength; |
(...skipping 29 matching lines...) Expand all Loading... |
698 targetIntervalStart, | 703 targetIntervalStart, |
699 _data.length); | 704 _data.length); |
700 return new Interval(targetIntervalStart, targetIntervalEnd.toInt()); | 705 return new Interval(targetIntervalStart, targetIntervalEnd.toInt()); |
701 } | 706 } |
702 } | 707 } |
703 | 708 |
704 /** | 709 /** |
705 * Simple list view class where each item has fixed width and height. | 710 * Simple list view class where each item has fixed width and height. |
706 */ | 711 */ |
707 class ListView<D> extends GenericListView<D> { | 712 class ListView<D> extends GenericListView<D> { |
708 | |
709 /** | 713 /** |
710 * Creates a new ListView for the given data. If [:_data:] is an | 714 * Creates a new ListView for the given data. If [:_data:] is an |
711 * [:ObservableList<T>:] then it will listen to changes to the list and | 715 * [:ObservableList<T>:] then it will listen to changes to the list and |
712 * update the view appropriately. | 716 * update the view appropriately. |
713 */ | 717 */ |
714 ListView(List<D> data, ViewFactory<D> itemViewFactory, bool scrollable, | 718 ListView(List<D> data, ViewFactory<D> itemViewFactory, bool scrollable, |
715 bool vertical, ObservableValue<D> selectedItem, | 719 bool vertical, ObservableValue<D> selectedItem, |
716 [bool snapToItems = false, | 720 [bool snapToItems = false, |
717 bool paginate = false, | 721 bool paginate = false, |
718 bool removeClippedViews = false, | 722 bool removeClippedViews = false, |
719 bool showScrollbar = false, | 723 bool showScrollbar = false, |
720 PageState pages = null]) | 724 PageState pages = null]) |
721 : super(new FixedSizeListViewLayout<D>(itemViewFactory, data, vertical, | 725 : super( |
722 paginate), | 726 new FixedSizeListViewLayout<D>( |
723 data, scrollable, vertical, selectedItem, snapToItems, paginate, | 727 itemViewFactory, data, vertical, paginate), |
724 removeClippedViews, showScrollbar, pages); | 728 data, |
| 729 scrollable, |
| 730 vertical, |
| 731 selectedItem, |
| 732 snapToItems, |
| 733 paginate, |
| 734 removeClippedViews, |
| 735 showScrollbar, |
| 736 pages); |
725 } | 737 } |
726 | 738 |
727 /** | 739 /** |
728 * Layout where each item may have variable size along the axis the list view | 740 * Layout where each item may have variable size along the axis the list view |
729 * extends. | 741 * extends. |
730 */ | 742 */ |
731 class VariableSizeListViewLayout<D> implements ListViewLayout<D> { | 743 class VariableSizeListViewLayout<D> implements ListViewLayout<D> { |
732 List<D> _data; | 744 List<D> _data; |
733 List<int> _itemOffsets; | 745 List<int> _itemOffsets; |
734 List<int> _lengths; | 746 List<int> _lengths; |
735 int _lastOffset = 0; | 747 int _lastOffset = 0; |
736 bool _vertical; | 748 bool _vertical; |
737 bool _paginate; | 749 bool _paginate; |
738 VariableSizeViewFactory<D> itemViewFactory; | 750 VariableSizeViewFactory<D> itemViewFactory; |
739 Interval _lastVisibleInterval; | 751 Interval _lastVisibleInterval; |
740 | 752 |
741 VariableSizeListViewLayout(this.itemViewFactory, data, this._vertical, | 753 VariableSizeListViewLayout( |
742 this._paginate) : | 754 this.itemViewFactory, data, this._vertical, this._paginate) |
743 _data = data, | 755 : _data = data, |
744 _lastVisibleInterval = new Interval(0, 0) { | 756 _lastVisibleInterval = new Interval(0, 0) { |
745 _itemOffsets = <int>[]; | 757 _itemOffsets = <int>[]; |
746 _lengths = <int>[]; | 758 _lengths = <int>[]; |
747 _itemOffsets.add(0); | 759 _itemOffsets.add(0); |
748 } | 760 } |
749 | 761 |
750 void onDataChange() { | 762 void onDataChange() { |
751 _itemOffsets.clear(); | 763 _itemOffsets.clear(); |
752 _itemOffsets.add(0); | 764 _itemOffsets.add(0); |
753 _lengths.clear(); | 765 _lengths.clear(); |
754 } | 766 } |
755 | 767 |
756 View newView(int index) => itemViewFactory.newView(_data[index]); | 768 View newView(int index) => itemViewFactory.newView(_data[index]); |
757 | 769 |
758 int getWidth(int viewLength) { | 770 int getWidth(int viewLength) { |
759 if (_vertical) { | 771 if (_vertical) { |
760 return itemViewFactory.getWidth(null); | 772 return itemViewFactory.getWidth(null); |
761 } else { | 773 } else { |
762 return getLength(viewLength); | 774 return getLength(viewLength); |
(...skipping 30 matching lines...) Expand all Loading... |
793 if (_lengths.length == _data.length) { | 805 if (_lengths.length == _data.length) { |
794 // No need to estimate... we have all the data already. | 806 // No need to estimate... we have all the data already. |
795 return getLength(viewLength); | 807 return getLength(viewLength); |
796 } | 808 } |
797 if (_itemOffsets.length > 1 && _lengths.length > 0) { | 809 if (_itemOffsets.length > 1 && _lengths.length > 0) { |
798 // Estimate length by taking the average of the lengths | 810 // Estimate length by taking the average of the lengths |
799 // of the known views. | 811 // of the known views. |
800 num lengthFromAllButLastElement = 0; | 812 num lengthFromAllButLastElement = 0; |
801 if (_itemOffsets.length > 2) { | 813 if (_itemOffsets.length > 2) { |
802 lengthFromAllButLastElement = | 814 lengthFromAllButLastElement = |
803 (getOffset(_itemOffsets.length - 2) - | 815 (getOffset(_itemOffsets.length - 2) - getOffset(0)) * |
804 getOffset(0)) * | 816 (_data.length / (_itemOffsets.length - 2)); |
805 (_data.length / (_itemOffsets.length - 2)); | |
806 } | 817 } |
807 return (lengthFromAllButLastElement + | 818 return (lengthFromAllButLastElement + |
808 Math.max(viewLength, _lengths[_lengths.length - 1])).toInt(); | 819 Math.max(viewLength, _lengths[_lengths.length - 1])) |
| 820 .toInt(); |
809 } else { | 821 } else { |
810 if (_lengths.length == 1) { | 822 if (_lengths.length == 1) { |
811 return Math.max(viewLength, _lengths[0]); | 823 return Math.max(viewLength, _lengths[0]); |
812 } else { | 824 } else { |
813 return viewLength; | 825 return viewLength; |
814 } | 826 } |
815 } | 827 } |
816 } | 828 } |
817 | 829 |
818 int getLength(int viewLength) { | 830 int getLength(int viewLength) { |
819 if (_data.length == 0) { | 831 if (_data.length == 0) { |
820 return viewLength; | 832 return viewLength; |
821 } else { | 833 } else { |
822 // Hack so that _lengths[length - 1] is available. | 834 // Hack so that _lengths[length - 1] is available. |
823 getOffset(_data.length); | 835 getOffset(_data.length); |
824 return (getOffset(_data.length - 1) - getOffset(0)) + | 836 return (getOffset(_data.length - 1) - getOffset(0)) + |
825 Math.max(_lengths[_lengths.length - 1], viewLength); | 837 Math.max(_lengths[_lengths.length - 1], viewLength); |
826 } | 838 } |
827 } | 839 } |
828 | 840 |
829 int getOffset(int index) { | 841 int getOffset(int index) { |
830 if (index >= _itemOffsets.length) { | 842 if (index >= _itemOffsets.length) { |
831 int offset = _itemOffsets[_itemOffsets.length - 1]; | 843 int offset = _itemOffsets[_itemOffsets.length - 1]; |
832 for (int i = _itemOffsets.length; i <= index; i++) { | 844 for (int i = _itemOffsets.length; i <= index; i++) { |
833 int length = _vertical ? itemViewFactory.getHeight(_data[i - 1]) | 845 int length = _vertical |
| 846 ? itemViewFactory.getHeight(_data[i - 1]) |
834 : itemViewFactory.getWidth(_data[i - 1]); | 847 : itemViewFactory.getWidth(_data[i - 1]); |
835 offset += length; | 848 offset += length; |
836 _itemOffsets.add(offset); | 849 _itemOffsets.add(offset); |
837 _lengths.add(length); | 850 _lengths.add(length); |
838 } | 851 } |
839 } | 852 } |
840 return _itemOffsets[index]; | 853 return _itemOffsets[index]; |
841 } | 854 } |
842 | 855 |
843 int getPage(int index, int viewLength) { | 856 int getPage(int index, int viewLength) { |
844 // TODO(jacobr): implement. | 857 // TODO(jacobr): implement. |
845 throw 'Not implemented'; | 858 throw 'Not implemented'; |
846 } | 859 } |
847 | 860 |
848 int getPageStartIndex(int page, int viewLength) { | 861 int getPageStartIndex(int page, int viewLength) { |
849 // TODO(jacobr): implement. | 862 // TODO(jacobr): implement. |
850 throw 'Not implemented'; | 863 throw 'Not implemented'; |
851 } | 864 } |
852 | 865 |
853 int getSnapIndex(num offset, int viewLength) { | 866 int getSnapIndex(num offset, int viewLength) { |
854 for (int i = 1; i < _data.length; i++) { | 867 for (int i = 1; i < _data.length; i++) { |
855 if (getOffset(i) + getOffset(i - 1) > -offset * 2) { | 868 if (getOffset(i) + getOffset(i - 1) > -offset * 2) { |
856 return i - 1; | 869 return i - 1; |
857 } | 870 } |
858 } | 871 } |
859 return _data.length - 1; | 872 return _data.length - 1; |
860 } | 873 } |
861 | 874 |
862 Interval computeVisibleInterval( | 875 Interval computeVisibleInterval( |
863 num offset, num viewLength, num bufferLength) { | 876 num offset, num viewLength, num bufferLength) { |
864 offset = offset.toInt(); | 877 offset = offset.toInt(); |
865 int start = _findFirstItemBefore( | 878 int start = _findFirstItemBefore(-offset - bufferLength, |
866 -offset - bufferLength, | |
867 _lastVisibleInterval != null ? _lastVisibleInterval.start : 0); | 879 _lastVisibleInterval != null ? _lastVisibleInterval.start : 0); |
868 int end = _findFirstItemAfter( | 880 int end = _findFirstItemAfter(-offset + viewLength + bufferLength, |
869 -offset + viewLength + bufferLength, | |
870 _lastVisibleInterval != null ? _lastVisibleInterval.end : 0); | 881 _lastVisibleInterval != null ? _lastVisibleInterval.end : 0); |
871 _lastVisibleInterval = new Interval(start, Math.max(start, end)); | 882 _lastVisibleInterval = new Interval(start, Math.max(start, end)); |
872 _lastOffset = offset; | 883 _lastOffset = offset; |
873 return _lastVisibleInterval; | 884 return _lastVisibleInterval; |
874 } | 885 } |
875 | 886 |
876 int _findFirstItemAfter(num target, int hint) { | 887 int _findFirstItemAfter(num target, int hint) { |
877 for (int i = 0; i < _data.length; i++) { | 888 for (int i = 0; i < _data.length; i++) { |
878 if (getOffset(i) > target) { | 889 if (getOffset(i) > target) { |
879 return i; | 890 return i; |
880 } | 891 } |
881 } | 892 } |
882 return _data.length; | 893 return _data.length; |
883 } | 894 } |
884 | 895 |
885 // TODO(jacobr): use hint. | 896 // TODO(jacobr): use hint. |
886 int _findFirstItemBefore(num target, int hint) { | 897 int _findFirstItemBefore(num target, int hint) { |
887 // We go search this direction delaying computing the actual view size | 898 // We go search this direction delaying computing the actual view size |
888 // as long as possible. | 899 // as long as possible. |
889 for (int i = 1; i < _data.length; i++) { | 900 for (int i = 1; i < _data.length; i++) { |
890 if (getOffset(i) >= target) { | 901 if (getOffset(i) >= target) { |
891 return i - 1; | 902 return i - 1; |
892 } | 903 } |
893 } | 904 } |
894 return Math.max(_data.length - 1, 0); | 905 return Math.max(_data.length - 1, 0); |
895 } | 906 } |
896 } | 907 } |
897 | 908 |
898 class VariableSizeListView<D> extends GenericListView<D> { | 909 class VariableSizeListView<D> extends GenericListView<D> { |
899 | 910 VariableSizeListView(List<D> data, VariableSizeViewFactory<D> itemViewFactory, |
900 VariableSizeListView(List<D> data, | 911 bool scrollable, bool vertical, ObservableValue<D> selectedItem, |
901 VariableSizeViewFactory<D> itemViewFactory, | 912 [bool snapToItems = false, |
902 bool scrollable, | 913 bool paginate = false, |
903 bool vertical, | 914 bool removeClippedViews = false, |
904 ObservableValue<D> selectedItem, | 915 bool showScrollbar = false, |
905 [bool snapToItems = false, | 916 PageState pages = null]) |
906 bool paginate = false, | 917 : super( |
907 bool removeClippedViews = false, | 918 new VariableSizeListViewLayout( |
908 bool showScrollbar = false, | 919 itemViewFactory, data, vertical, paginate), |
909 PageState pages = null]) | 920 data, |
910 : super(new VariableSizeListViewLayout(itemViewFactory, data, vertical, | 921 scrollable, |
911 paginate), | 922 vertical, |
912 data, scrollable, vertical, selectedItem, snapToItems, | 923 selectedItem, |
913 paginate, removeClippedViews, showScrollbar, pages); | 924 snapToItems, |
| 925 paginate, |
| 926 removeClippedViews, |
| 927 showScrollbar, |
| 928 pages); |
914 } | 929 } |
915 | 930 |
916 /** A back button that is equivalent to clicking "back" in the browser. */ | 931 /** A back button that is equivalent to clicking "back" in the browser. */ |
917 class BackButton extends View { | 932 class BackButton extends View { |
918 BackButton() : super(); | 933 BackButton() : super(); |
919 | 934 |
920 Element render() => new Element.html('<div class="back-arrow button"></div>'); | 935 Element render() => new Element.html('<div class="back-arrow button"></div>'); |
921 | 936 |
922 void afterRender(Element node) { | 937 void afterRender(Element node) { |
923 addOnClick((e) => window.history.back()); | 938 addOnClick((e) => window.history.back()); |
924 } | 939 } |
925 } | 940 } |
926 | 941 |
927 | |
928 // TODO(terry): Maybe should be part of ButtonView class in appstack/view? | 942 // TODO(terry): Maybe should be part of ButtonView class in appstack/view? |
929 /** OS button. */ | 943 /** OS button. */ |
930 class PushButtonView extends View { | 944 class PushButtonView extends View { |
931 final String _text; | 945 final String _text; |
932 final String _cssClass; | 946 final String _cssClass; |
933 final _clickHandler; | 947 final _clickHandler; |
934 | 948 |
935 PushButtonView(this._text, this._cssClass, this._clickHandler) : super(); | 949 PushButtonView(this._text, this._cssClass, this._clickHandler) : super(); |
936 | 950 |
937 Element render() { | 951 Element render() { |
938 return new Element.html('<button class="${_cssClass}">${_text}</button>'); | 952 return new Element.html('<button class="${_cssClass}">${_text}</button>'); |
939 } | 953 } |
940 | 954 |
941 void afterRender(Element node) { | 955 void afterRender(Element node) { |
942 addOnClick(_clickHandler); | 956 addOnClick(_clickHandler); |
943 } | 957 } |
944 } | 958 } |
945 | 959 |
946 | |
947 // TODO(terry): Add a drop shadow around edge and corners need to be rounded. | 960 // TODO(terry): Add a drop shadow around edge and corners need to be rounded. |
948 // Need to support conveyor for contents of dialog so it's not | 961 // Need to support conveyor for contents of dialog so it's not |
949 // larger than the parent window. | 962 // larger than the parent window. |
950 /** A generic dialog view supports title, done button and dialog content. */ | 963 /** A generic dialog view supports title, done button and dialog content. */ |
951 class DialogView extends View { | 964 class DialogView extends View { |
952 final String _title; | 965 final String _title; |
953 final String _cssName; | 966 final String _cssName; |
954 final View _content; | 967 final View _content; |
955 Element container; | 968 Element container; |
956 PushButtonView _done; | 969 PushButtonView _done; |
957 | 970 |
958 DialogView(this._title, this._cssName, this._content) : super() {} | 971 DialogView(this._title, this._cssName, this._content) : super() {} |
959 | 972 |
960 Element render() { | 973 Element render() { |
961 final node = new Element.html(''' | 974 final node = new Element.html(''' |
962 <div class="dialog-modal"> | 975 <div class="dialog-modal"> |
963 <div class="dialog $_cssName"> | 976 <div class="dialog $_cssName"> |
964 <div class="dialog-title-area"> | 977 <div class="dialog-title-area"> |
965 <span class="dialog-title">$_title</span> | 978 <span class="dialog-title">$_title</span> |
966 </div> | 979 </div> |
967 <div class="dialog-body"></div> | 980 <div class="dialog-body"></div> |
968 </div> | 981 </div> |
969 </div>'''); | 982 </div>'''); |
970 | 983 |
971 _done = new PushButtonView('Done', 'done-button', | 984 _done = new PushButtonView( |
972 EventBatch.wrap((e) => onDone())); | 985 'Done', 'done-button', EventBatch.wrap((e) => onDone())); |
973 final titleArea = node.querySelector('.dialog-title-area'); | 986 final titleArea = node.querySelector('.dialog-title-area'); |
974 titleArea.nodes.add(_done.node); | 987 titleArea.nodes.add(_done.node); |
975 | 988 |
976 container = node.querySelector('.dialog-body'); | 989 container = node.querySelector('.dialog-body'); |
977 container.nodes.add(_content.node); | 990 container.nodes.add(_content.node); |
978 | 991 |
979 return node; | 992 return node; |
980 } | 993 } |
981 | 994 |
982 /** Override to handle dialog done. */ | 995 /** Override to handle dialog done. */ |
983 void onDone() { } | 996 void onDone() {} |
984 } | 997 } |
OLD | NEW |