LCOV - code coverage report
Current view: top level - lib/src/components - component_set.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 35 45 77.8 %
Date: 2021-08-10 15:50:53 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:ordered_set/comparing.dart';
       2             : import 'package:ordered_set/queryable_ordered_set.dart';
       3             : 
       4             : import '../../components.dart';
       5             : import '../game/base_game.dart';
       6             : 
       7             : /// This is a simple wrapper over [QueryableOrderedSet] to be used by
       8             : /// [BaseGame] and [BaseComponent].
       9             : ///
      10             : /// Instead of immediately modifying the component list, all insertion
      11             : /// and removal operations are queued to be performed on the next tick.
      12             : ///
      13             : /// This will avoid any concurrent modification exceptions while the game
      14             : /// iterates through the component list.
      15             : ///
      16             : /// This wrapper also guaranteed that prepare, onLoad, onMount and all the
      17             : /// lifecycle methods are called properly.
      18             : class ComponentSet extends QueryableOrderedSet<Component> {
      19             :   /// Components to be added on the next update.
      20             :   ///
      21             :   /// The component list is only changed at the start of each update to avoid
      22             :   /// concurrency issues.
      23             :   final List<Component> _addLater = [];
      24             : 
      25             :   /// Components to be removed on the next update.
      26             :   ///
      27             :   /// The component list is only changed at the start of each update to avoid
      28             :   /// concurrency issues.
      29             :   final Set<Component> _removeLater = {};
      30             : 
      31             :   /// This is the "prepare" function that will be called *before* the
      32             :   /// component is added to the component list by the add/addAll methods.
      33             :   final void Function(Component child, {BaseGame? gameRef}) prepare;
      34             : 
      35          25 :   ComponentSet(
      36             :     int Function(Component e1, Component e2)? compare,
      37             :     this.prepare,
      38          25 :   ) : super(compare);
      39             : 
      40             :   /// Prepares and registers one component to be added on the next game tick.
      41             :   ///
      42             :   /// This is the interface compliant version; if you want to provide an
      43             :   /// explicit gameRef or await for the onLoad, use [addChild].
      44             :   ///
      45             :   /// Note: the component is only added on the next tick. This method always
      46             :   /// returns true.
      47           0 :   @override
      48             :   bool add(Component c) {
      49           0 :     addChild(c);
      50             :     return true;
      51             :   }
      52             : 
      53             :   /// Prepares and registers a list of components to be added on the next game
      54             :   /// tick.
      55             :   ///
      56             :   /// This is the interface compliant version; if you want to provide an
      57             :   /// explicit gameRef or await for the onLoad, use [addChild].
      58             :   ///
      59             :   /// Note: the components are only added on the next tick. This method always
      60             :   /// returns the total lenght of the provided list.
      61           0 :   @override
      62             :   int addAll(Iterable<Component> components) {
      63           0 :     addChildren(components);
      64           0 :     return components.length;
      65             :   }
      66             : 
      67             :   /// Prepares and registers one component to be added on the next game tick.
      68             :   ///
      69             :   /// This allows you to provide a specific gameRef if this component is being
      70             :   /// added from within another component that is already on a BaseGame.
      71             :   /// You can await for the onLoad function, if present.
      72             :   /// This method can be considered sync for all intents and purposes if no
      73             :   /// onLoad is provided by the component.
      74          24 :   Future<void> addChild(Component c, {BaseGame? gameRef}) async {
      75          48 :     prepare(c, gameRef: gameRef);
      76             : 
      77          24 :     final loadFuture = c.onLoad();
      78             :     if (loadFuture != null) {
      79           3 :       await loadFuture;
      80             :     }
      81             : 
      82          48 :     _addLater.add(c);
      83             :   }
      84             : 
      85             :   /// Prepares and registers a list of component to be added on the next game
      86             :   /// tick.
      87             :   ///
      88             :   /// See [addChild] for more details.
      89           5 :   Future<void> addChildren(
      90             :     Iterable<Component> components, {
      91             :     BaseGame? gameRef,
      92             :   }) async {
      93          15 :     final ps = components.map((c) => addChild(c, gameRef: gameRef));
      94          10 :     await Future.wait(ps);
      95             :   }
      96             : 
      97             :   /// Marks a component to be removed from the components list on the next game
      98             :   /// tick.
      99           2 :   @override
     100             :   bool remove(Component c) {
     101           4 :     _removeLater.add(c);
     102             :     return true;
     103             :   }
     104             : 
     105             :   /// Marks a list of components to be removed from the components list on the
     106             :   /// next game tick.
     107           0 :   void removeAll(Iterable<Component> components) {
     108           0 :     _removeLater.addAll(components);
     109             :   }
     110             : 
     111             :   /// Marks all existing components to be removed from the components list on
     112             :   /// the next game tick.
     113           1 :   @override
     114             :   void clear() {
     115           2 :     _removeLater.addAll(this);
     116             :   }
     117             : 
     118             :   /// Materializes the component list in reversed order.
     119           4 :   Iterable<Component> reversed() {
     120           8 :     return toList().reversed;
     121             :   }
     122             : 
     123             :   /// Call this on your update method.
     124             :   ///
     125             :   /// This method effectuates any pending operations of insertion or removal,
     126             :   /// and thus actually modifies the components set.
     127             :   /// Note: do not call this while iterating the set.
     128          21 :   void updateComponentList() {
     129          93 :     _removeLater.addAll(where((c) => c.shouldRemove));
     130          47 :     _removeLater.forEach((c) {
     131           5 :       c.onRemove();
     132           5 :       super.remove(c);
     133             :     });
     134          42 :     _removeLater.clear();
     135             : 
     136          42 :     if (_addLater.isNotEmpty) {
     137          40 :       final addNow = _addLater.toList(growable: false);
     138          40 :       _addLater.clear();
     139          40 :       addNow.forEach((c) {
     140          20 :         super.add(c);
     141          20 :         c.onMount();
     142             :       });
     143             :     }
     144             :   }
     145             : 
     146           1 :   @override
     147             :   void rebalanceAll() {
     148           1 :     final elements = toList();
     149             :     // bypass the wrapper because the components are already added
     150           1 :     super.clear();
     151           1 :     elements.forEach(super.add);
     152             :   }
     153             : 
     154           0 :   @override
     155             :   void rebalanceWhere(bool Function(Component element) test) {
     156             :     // bypass the wrapper because the components are already added
     157           0 :     final elements = super.removeWhere(test).toList();
     158           0 :     elements.forEach(super.add);
     159             :   }
     160             : 
     161             :   /// Creates a [ComponentSet] with a default value for the compare function,
     162             :   /// using the Component's priority for sorting.
     163             :   ///
     164             :   /// You must still provide your [prepare] function depending on the context.
     165          25 :   static ComponentSet createDefault(
     166             :     void Function(Component child, {BaseGame? gameRef}) prepare,
     167             :   ) {
     168          25 :     return ComponentSet(
     169          65 :       Comparing.on<Component>((c) => c.priority),
     170             :       prepare,
     171             :     );
     172             :   }
     173             : }

Generated by: LCOV version 1.15