| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:async'; |
| |
| import 'package:observatory_2/src/elements/helpers/rendering_queue.dart'; |
| export 'package:observatory_2/src/elements/helpers/rendering_queue.dart'; |
| |
| /// A generic renderable object. |
| abstract class Renderable { |
| void render(); |
| } |
| |
| /// Event related to a Renderable rendering phase. |
| class RenderedEvent<T> { |
| /// Renderable to which the event is related |
| final T element; |
| |
| /// Is another rendering scheduled for this element. |
| final bool otherRenderScheduled; |
| |
| RenderedEvent(this.element, this.otherRenderScheduled) { |
| assert(element != null); |
| assert(otherRenderScheduled != null); |
| } |
| } |
| |
| /// Scheduler for rendering operations. |
| class RenderingScheduler<T extends Renderable> implements RenderingTask { |
| bool _enabled = false; |
| bool _dirty = false; |
| bool _renderingScheduled = false; |
| bool _notificationScheduled = false; |
| bool _waitForBarrier = false; |
| |
| /// Element managed by this scheduler. |
| final T element; |
| |
| /// Queue used for rendering operations. |
| final RenderingQueue queue; |
| |
| final List<Future> _wait = <Future>[]; |
| |
| /// Does the element need a new rendering cycle. |
| bool get isDirty => _dirty; |
| |
| /// Is the scheduler enabled. |
| bool get isEnabled => _enabled; |
| |
| final StreamController<RenderedEvent<T>> _onRendered = |
| new StreamController<RenderedEvent<T>>.broadcast(); |
| Stream<RenderedEvent<T>> get onRendered => _onRendered.stream; |
| |
| /// Creates a new scheduler for an element. |
| /// If no queue is provided it will create a new default configured queue. |
| factory RenderingScheduler(T element, {RenderingQueue queue}) { |
| assert(element != null); |
| if (queue == null) { |
| queue = new RenderingQueue(); |
| } |
| return new RenderingScheduler<T>._(element, queue); |
| } |
| |
| RenderingScheduler._(this.element, this.queue); |
| |
| /// Enable the scheduler. |
| /// New dirty or schedule request will be considered. |
| void enable() { |
| if (_enabled) return; |
| _enabled = true; |
| scheduleRendering(); |
| } |
| |
| /// Disable the scheduler. |
| /// New dirty or schedule request will be discarded. |
| /// [optional] notify: send a final RenderEvent. |
| void disable({bool notify: false}) { |
| assert(notify != null); |
| if (!_enabled) return; |
| _enabled = false; |
| if (notify) scheduleNotification(); |
| } |
| |
| /// Set the object as dirty. A rendering will be scheduled. |
| void dirty() { |
| if (_dirty) return; |
| _dirty = true; |
| scheduleRendering(); |
| } |
| |
| /// Checks for modification during attribute set. |
| /// If value changes a new rendering is scheduled. |
| /// set attr(T v) => _attr = _r.checkAndReact(_attr, v); |
| T checkAndReact<T>(T oldValue, T newValue) { |
| if (oldValue != newValue) |
| dirty(); |
| else |
| scheduleNotification(); |
| return newValue; |
| } |
| |
| /// Schedules a new rendering phase. |
| void scheduleRendering() { |
| if (_renderingScheduled) return; |
| if (!_enabled) return; |
| queue.enqueue(this, waitForBarrier: _waitForBarrier); |
| _waitForBarrier = true; |
| _renderingScheduled = true; |
| } |
| |
| /// Renders the element (if the scheduler is enabled). |
| /// It will clear the dirty flag. |
| void render() { |
| _renderingScheduled = false; |
| if (!_enabled) return; |
| _dirty = false; |
| _wait.clear(); |
| element.render(); |
| scheduleNotification(); |
| if (_dirty) scheduleRendering(); |
| } |
| |
| /// Schedules a notification. |
| void scheduleNotification() { |
| if (_notificationScheduled) return; |
| _notify(); |
| _notificationScheduled = true; |
| } |
| |
| void waitFor(Iterable<Future> it) { |
| _wait.addAll(it); |
| } |
| |
| Future _notify() async { |
| await Future.wait(_wait); |
| _wait.clear(); |
| _onRendered.add(new RenderedEvent<T>(element, _dirty)); |
| _notificationScheduled = false; |
| } |
| } |