API Reference 0.7.1rikulo_view_implRunOnceViewManager

RunOnceViewManager class

A run-once view manager is used to manage the view-handling task in a way that the task will be executed only once for each view .

To use it, first you assign the view-handling task in the constructor. Then, you can invoke queue to put a view into the queue. The view won't be handled immediately. Rather it will be handled later automatically. Moreover, if the view is queued multiple times before it was handled, the task is invoked only once for the given view. That is why it is called run-once.

In additions, you can control whether to ignore the view, if the view is not attached to the document. Or, to ignore the view if one of its ancestor is also queued.

To enforce the views queued in this manager to be handled immediately, you can invoke flush.

class RunOnceViewManager {
 final RunOnceQueue _runQue;
 final Set<View> _views;
 final _ViewTask _task;
 final List<RunOnceReadyCheck> _readyChecks;
 final bool _ignoreDetached;
 final bool _ignoreSubviews;

 /** Constructor.
  *
  * + [task] is the view-handling task. Notice that if you pass null here, you have
  * override [handle_] to handle it.
  * + [ignoreDetached] specifies whether to ignore the views that are not attached
  * to the docuemnt (ie.., ignore views if [View.inDocument] is false).
  * + [ignoreSubviews] specifies whether to ignore the sub views. In other words,
  * a view will be ignored, if one of its ancestor has been queued for handling too.
  */
 RunOnceViewManager(void task(View view),
 {bool ignoreDetached: true, bool ignoreSubviews: true}):
 _runQue = new RunOnceQueue(), _views = new Set(), _readyChecks = new List(),
 _task = task, _ignoreDetached = ignoreDetached, _ignoreSubviews = ignoreSubviews {
 }

 /** Returns if there is no view is queued.
  *
  * + [view] if specified, this method check only if the given view and its descendant
  * views is in the queue.
  */
 bool isQueueEmpty([View view]) {
   if (view == null)
     return _views.isEmpty;

   for (final v in _views)
     if (v.isDescendantOf(view))
       return false;
   return true;
 }
 /** Adds the given view to the queue.
  */
 void queue(View view) {
   //we don't check view.inDocument here since it might be attached later
   _views.add(view);
   _runQue.add("", () {flush();}, 0);
 }
 /** Removes the given view from the queue, so the action won't take place.
  */
 void unqueue(View view) {
   _views.remove(view);
 }
 /** Hanldes the give view, if not null, or
  * all queued views, if the give view is null.
  *
  * + [force] specifies whether to force the tasks registered with [addReadyCheck]
  */
 void flush([View view, bool force=false]) {
   if (!_ready(view, force)) {
     if (view != null)
       _views.add(view);
   } else if (view != null) {
     _flushOne(view, force);
   } else {
     _flushAll();
   }
 }
 /** Handles the given view.
  * Don't call this method directly. It is called automatically
  * when a view is required to handle.
  *
  * Default: invoke the task that was assigned in the constructor.
  *
  * You can override this method if you don't pass a task in the constructor.
  */
 void handle_(View view) {
   _task(view);
 }

 void _flushAll() {
   //remove redundent
   final List<View> toRemove = [];
   for (final view in _views) {
     if (_ignoreDetached && !view.inDocument) {
       toRemove.add(view); //ignore detached
       continue;
     }

     if (_ignoreSubviews) {
       for (View v = view; (v = v.parent) != null;) {
         if (_views.contains(v)) {//view is subview of v
           toRemove.add(view);  //ignore subview (i.e., view)
           break;
         }
       }
     }
   }
   for (final view in toRemove)
     _views.remove(view);

   //1. handle non-achored roots, 2. handle anchored root, 3. handle others
   final root1 = [], root2 = [], others = [];
   for (final v in _views)
     (v.parent != null ? others: v.profile.anchorView == null ? root1: root2).add(v);
   _views.clear();

   for (final v in root1)
     handle_(v);
   for (final v in root2)
     handle_(v);
   for (final v in others)
     handle_(v);
 }
 void _flushOne(View view, bool force) {
   final found = _views.remove(view);
   if (!_ignoreDetached || view.inDocument) {
     if (_ignoreSubviews) {
       if (!force)
         for (View v = view; (v = v.parent) != null;)
           if (_views.contains(v)) //view is subview of v
             return; //no need to do since the parent will handle it (later)

       final List<View> toRemove = [];
       for (final View v in _views)
         if (v.isDescendantOf(view))
           toRemove.add(v);
       for (final v in toRemove)
         _views.remove(v);

       handle_(view);
     } else {
       List<View> todos = [];
       if (found)
         todos.add(view);

       for (final v in _views)
         if (v.isDescendantOf(view))
           todos.add(v);

       for (final v in todos) {
         _views.remove(v);
         handle_(v);
       }
     }
   }
 }
 /** Adds a callback to check if this manager can handle the views (i.e.,
  * whether it has to wait).
  * It is used if you'd like this manage to depend on other conditions.
  */
 void addReadyCheck(RunOnceReadyCheck ready) {
   _readyChecks.add(ready);
 }
 bool _ready(View view, bool force) {
   if (!_readyChecks.isEmpty) {
     final _Task continueTask = () {flush(view, force);};
     for (final RunOnceReadyCheck ready in _readyChecks)
       if (!ready(view, continueTask, force))
         return false;
   }
   return true;
 }
}

Subclasses

LayoutManager

Constructors

new RunOnceViewManager(void task(View view), {bool ignoreDetached: true, bool ignoreSubviews: true}) #

Constructor.

  • task is the view-handling task. Notice that if you pass null here, you have override handle_ to handle it.

  • ignoreDetached specifies whether to ignore the views that are not attached to the docuemnt (ie.., ignore views if View.inDocument is false).

  • ignoreSubviews specifies whether to ignore the sub views. In other words, a view will be ignored, if one of its ancestor has been queued for handling too.

RunOnceViewManager(void task(View view),
{bool ignoreDetached: true, bool ignoreSubviews: true}):
_runQue = new RunOnceQueue(), _views = new Set(), _readyChecks = new List(),
_task = task, _ignoreDetached = ignoreDetached, _ignoreSubviews = ignoreSubviews {
}

Methods

void addReadyCheck(RunOnceReadyCheck ready) #

Adds a callback to check if this manager can handle the views (i.e., whether it has to wait). It is used if you'd like this manage to depend on other conditions.

void addReadyCheck(RunOnceReadyCheck ready) {
 _readyChecks.add(ready);
}

void flush([View view, bool force = false]) #

Hanldes the give view, if not null, or all queued views, if the give view is null.

  • force specifies whether to force the tasks registered with addReadyCheck
void flush([View view, bool force=false]) {
 if (!_ready(view, force)) {
   if (view != null)
     _views.add(view);
 } else if (view != null) {
   _flushOne(view, force);
 } else {
   _flushAll();
 }
}

void handle_(View view) #

Handles the given view. Don't call this method directly. It is called automatically when a view is required to handle.

Default: invoke the task that was assigned in the constructor.

You can override this method if you don't pass a task in the constructor.

void handle_(View view) {
 _task(view);
}

bool isQueueEmpty([View view]) #

Returns if there is no view is queued.

  • view if specified, this method check only if the given view and its descendant views is in the queue.

bool isQueueEmpty([View view]) {
 if (view == null)
   return _views.isEmpty;

 for (final v in _views)
   if (v.isDescendantOf(view))
     return false;
 return true;
}

void queue(View view) #

Adds the given view to the queue.

void queue(View view) {
 //we don't check view.inDocument here since it might be attached later
 _views.add(view);
 _runQue.add("", () {flush();}, 0);
}

void unqueue(View view) #

Removes the given view from the queue, so the action won't take place.

void unqueue(View view) {
 _views.remove(view);
}