OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library observe.src.path_observer; | 5 library observe.src.path_observer; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:collection'; | 8 import 'dart:collection'; |
9 import 'dart:math' show min; | 9 import 'dart:math' show min; |
10 | 10 |
(...skipping 23 matching lines...) Expand all Loading... |
34 /// See [open] and [value]. | 34 /// See [open] and [value]. |
35 PathObserver(Object object, [path]) | 35 PathObserver(Object object, [path]) |
36 : _object = object, | 36 : _object = object, |
37 _path = path is PropertyPath ? path : new PropertyPath(path); | 37 _path = path is PropertyPath ? path : new PropertyPath(path); |
38 | 38 |
39 bool get _isClosed => _path == null; | 39 bool get _isClosed => _path == null; |
40 | 40 |
41 /// Sets the value at this path. | 41 /// Sets the value at this path. |
42 void set value(Object newValue) { | 42 void set value(Object newValue) { |
43 if (_path != null) _path.setValueFrom(_object, newValue); | 43 if (_path != null) _path.setValueFrom(_object, newValue); |
| 44 _discardChanges(); |
44 } | 45 } |
45 | 46 |
46 int get _reportArgumentCount => 2; | 47 int get _reportArgumentCount => 2; |
47 | 48 |
48 /// Initiates observation and returns the initial value. | 49 /// Initiates observation and returns the initial value. |
49 /// The callback will be passed the updated [value], and may optionally be | 50 /// The callback will be passed the updated [value], and may optionally be |
50 /// declared to take a second argument, which will contain the previous value. | 51 /// declared to take a second argument, which will contain the previous value. |
51 open(callback) => super.open(callback); | 52 open(callback) => super.open(callback); |
52 | 53 |
53 void _connect() { | 54 void _connect() { |
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 | 425 |
425 if (path is! PropertyPath) path = new PropertyPath(path); | 426 if (path is! PropertyPath) path = new PropertyPath(path); |
426 _observed..add(object)..add(path); | 427 _observed..add(object)..add(path); |
427 } | 428 } |
428 | 429 |
429 void addObserver(Bindable observer) { | 430 void addObserver(Bindable observer) { |
430 if (_isOpen || _isClosed) { | 431 if (_isOpen || _isClosed) { |
431 throw new StateError('Cannot add observers once started.'); | 432 throw new StateError('Cannot add observers once started.'); |
432 } | 433 } |
433 | 434 |
434 observer.open(_deliver); | 435 observer.open((_) => deliver()); |
435 _observed..add(_observerSentinel)..add(observer); | 436 _observed..add(_observerSentinel)..add(observer); |
436 } | 437 } |
437 | 438 |
438 void _iterateObjects(void observe(obj)) { | 439 void _iterateObjects(void observe(obj)) { |
439 for (var i = 0; i < _observed.length; i += 2) { | 440 for (var i = 0; i < _observed.length; i += 2) { |
440 var object = _observed[i]; | 441 var object = _observed[i]; |
441 if (!identical(object, _observerSentinel)) { | 442 if (!identical(object, _observerSentinel)) { |
442 (_observed[i + 1] as PropertyPath)._iterateObjects(object, observe); | 443 (_observed[i + 1] as PropertyPath)._iterateObjects(object, observe); |
443 } | 444 } |
444 } | 445 } |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
525 'fewer arguments'); | 526 'fewer arguments'); |
526 } | 527 } |
527 | 528 |
528 _notifyCallback = callback; | 529 _notifyCallback = callback; |
529 _notifyArgumentCount = min(_reportArgumentCount, smoke.maxArgs(callback)); | 530 _notifyArgumentCount = min(_reportArgumentCount, smoke.maxArgs(callback)); |
530 | 531 |
531 _connect(); | 532 _connect(); |
532 return _value; | 533 return _value; |
533 } | 534 } |
534 | 535 |
535 get value { | 536 get value => _discardChanges(); |
536 _check(skipChanges: true); | |
537 return _value; | |
538 } | |
539 | 537 |
540 void close() { | 538 void close() { |
541 if (!_isOpen) return; | 539 if (!_isOpen) return; |
542 | 540 |
543 _disconnect(); | 541 _disconnect(); |
544 _value = null; | 542 _value = null; |
545 _notifyCallback = null; | 543 _notifyCallback = null; |
546 } | 544 } |
547 | 545 |
548 void _deliver(_) { | 546 _discardChanges() { |
549 if (_isOpen) _dirtyCheck(); | 547 _check(skipChanges: true); |
| 548 return _value; |
550 } | 549 } |
551 | 550 |
| 551 bool deliver() => _isOpen ? _dirtyCheck() : false; |
| 552 |
552 bool _dirtyCheck() { | 553 bool _dirtyCheck() { |
553 var cycles = 0; | 554 var cycles = 0; |
554 while (cycles < _MAX_DIRTY_CHECK_CYCLES && _check()) { | 555 while (cycles < _MAX_DIRTY_CHECK_CYCLES && _check()) { |
555 cycles++; | 556 cycles++; |
556 } | 557 } |
557 return cycles > 0; | 558 return cycles > 0; |
558 } | 559 } |
559 | 560 |
560 void _report(newValue, oldValue, [extraArg]) { | 561 void _report(newValue, oldValue, [extraArg]) { |
561 try { | 562 try { |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
670 for (var observer in _observers.values.toList(growable: false)) { | 671 for (var observer in _observers.values.toList(growable: false)) { |
671 if (observer._isOpen) observer._check(); | 672 if (observer._isOpen) observer._check(); |
672 } | 673 } |
673 | 674 |
674 _resetNeeded = true; | 675 _resetNeeded = true; |
675 scheduleMicrotask(reset); | 676 scheduleMicrotask(reset); |
676 } | 677 } |
677 } | 678 } |
678 | 679 |
679 const int _MAX_DIRTY_CHECK_CYCLES = 1000; | 680 const int _MAX_DIRTY_CHECK_CYCLES = 1000; |
OLD | NEW |