Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: pkg/observe/lib/src/observable_list.dart

Issue 19771010: implement dirty checking for @observable objects (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 part of observe; 5 part of observe;
6 6
7 /** 7 /**
8 * Represents an observable list of model values. If any items are added, 8 * Represents an observable list of model values. If any items are added,
9 * removed, or replaced, then observers that are listening to [changes] 9 * removed, or replaced, then observers that are listening to [changes]
10 * will be notified. 10 * will be notified.
11 */ 11 */
12 // TODO(jmesserly): remove implements List<E> once we can extend ListBase<E> 12 // TODO(jmesserly): remove implements List<E> once we can extend ListBase<E>
13 class ObservableList<E> extends _ListBaseWorkaround with ObservableMixin 13 class ObservableList<E> extends ListBase<E> with ChangeNotifierMixin {
14 implements List<E> { 14 List<ListChangeRecord> _listRecords;
15 List<ListChangeRecord> _records;
16 15
17 /** The inner [List<E>] with the actual storage. */ 16 /** The inner [List<E>] with the actual storage. */
18 final List<E> _list; 17 final List<E> _list;
19 18
20 /** 19 /**
21 * Creates an observable list of the given [length]. 20 * Creates an observable list of the given [length].
22 * 21 *
23 * If no [length] argument is supplied an extendable list of 22 * If no [length] argument is supplied an extendable list of
24 * length 0 is created. 23 * length 0 is created.
25 * 24 *
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 void _rangeCheck(int start, int end) { 173 void _rangeCheck(int start, int end) {
175 if (start < 0 || start > this.length) { 174 if (start < 0 || start > this.length) {
176 throw new RangeError.range(start, 0, this.length); 175 throw new RangeError.range(start, 0, this.length);
177 } 176 }
178 if (end < start || end > this.length) { 177 if (end < start || end > this.length) {
179 throw new RangeError.range(end, start, this.length); 178 throw new RangeError.range(end, start, this.length);
180 } 179 }
181 } 180 }
182 181
183 void _recordChange(ListChangeRecord record) { 182 void _recordChange(ListChangeRecord record) {
184 if (_records == null) { 183 if (_listRecords == null) {
185 _records = []; 184 _listRecords = [];
186 queueChangeRecords(_summarizeRecords); 185 runAsync(deliverChanges);
187 } 186 }
188 _records.add(record); 187 _listRecords.add(record);
188 }
189
190 bool deliverChanges() {
191 if (_listRecords == null) return false;
192 _summarizeRecords();
193 return super.deliverChanges();
189 } 194 }
190 195
191 /** 196 /**
192 * We need to summarize change records. Consumers of these records want to 197 * We need to summarize change records. Consumers of these records want to
193 * apply the batch sequentially, and ensure that they can find inserted 198 * apply the batch sequentially, and ensure that they can find inserted
194 * items by looking at that position in the list. This property does not 199 * items by looking at that position in the list. This property does not
195 * hold in our record-as-you-go records. Consider: 200 * hold in our record-as-you-go records. Consider:
196 * 201 *
197 * var model = toObservable(['a', 'b']); 202 * var model = toObservable(['a', 'b']);
198 * model.removeAt(1); 203 * model.removeAt(1);
(...skipping 10 matching lines...) Expand all
209 * new list of changes records. This has the advantage of not depending 214 * new list of changes records. This has the advantage of not depending
210 * on the actual *values*, so we don't need to perform N^2 edit 215 * on the actual *values*, so we don't need to perform N^2 edit
211 */ 216 */
212 // TODO(jmesserly): there's probably something smarter here, but this 217 // TODO(jmesserly): there's probably something smarter here, but this
213 // algorithm is pretty simple. It has complexity equivalent to the original 218 // algorithm is pretty simple. It has complexity equivalent to the original
214 // list modifications. 219 // list modifications.
215 // One simple idea: we can simply update the index map as we do the operations 220 // One simple idea: we can simply update the index map as we do the operations
216 // to the list, then produce the records at the end. 221 // to the list, then produce the records at the end.
217 void _summarizeRecords() { 222 void _summarizeRecords() {
218 int oldLength = length; 223 int oldLength = length;
219 for (var r in _records) { 224 for (var r in _listRecords) {
220 oldLength += r.removedCount - r.addedCount; 225 oldLength += r.removedCount - r.addedCount;
221 } 226 }
222 227
223 if (length != oldLength) { 228 if (length != oldLength) {
224 notifyPropertyChange(const Symbol('length'), oldLength, length); 229 notifyPropertyChange(const Symbol('length'), oldLength, length);
225 } 230 }
226 231
227 if (_records.length == 1) { 232 if (_listRecords.length == 1) {
228 notifyChange(_records[0]); 233 notifyChange(_listRecords[0]);
229 _records = null; 234 _listRecords = null;
230 return; 235 return;
231 } 236 }
232 237
233 var items = []; 238 var items = [];
234 for (int i = 0; i < oldLength; i++) items.add(i); 239 for (int i = 0; i < oldLength; i++) items.add(i);
235 for (var r in _records) { 240 for (var r in _listRecords) {
236 items.removeRange(r.index, r.index + r.removedCount); 241 items.removeRange(r.index, r.index + r.removedCount);
237 242
238 // Represent inserts with -1. 243 // Represent inserts with -1.
239 items.insertAll(r.index, new List.filled(r.addedCount, -1)); 244 items.insertAll(r.index, new List.filled(r.addedCount, -1));
240 } 245 }
241 assert(items.length == length); 246 assert(items.length == length);
242 247
243 _records = null; 248 _listRecords = null;
244 249
245 int index = 0; 250 int index = 0;
246 int offset = 0; 251 int offset = 0;
247 while (index < items.length) { 252 while (index < items.length) {
248 // Skip unchanged items. 253 // Skip unchanged items.
249 while (index < items.length && items[index] == index + offset) { 254 while (index < items.length && items[index] == index + offset) {
250 index++; 255 index++;
251 } 256 }
252 257
253 // Find inserts 258 // Find inserts
(...skipping 13 matching lines...) Expand all
267 272
268 if (added > 0 || removed > 0) { 273 if (added > 0 || removed > 0) {
269 notifyChange(new ListChangeRecord(startIndex, addedCount: added, 274 notifyChange(new ListChangeRecord(startIndex, addedCount: added,
270 removedCount: removed)); 275 removedCount: removed));
271 } 276 }
272 277
273 offset += removed - added; 278 offset += removed - added;
274 } 279 }
275 } 280 }
276 } 281 }
277
278 // TODO(jmesserly): bogus type to workaround spurious VM bug with generic base
279 // class and mixins.
280 abstract class _ListBaseWorkaround extends ListBase<dynamic> {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698