binding.dart 5.6 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 The Chromium Authors. 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:sky' as sky;

7
import 'package:sky/animation.dart';
Adam Barth's avatar
Adam Barth committed
8
import 'package:sky/gestures.dart';
9
import 'package:sky/src/rendering/box.dart';
Adam Barth's avatar
Adam Barth committed
10
import 'package:sky/src/rendering/hit_test.dart';
11 12
import 'package:sky/src/rendering/object.dart';
import 'package:sky/src/rendering/view.dart';
13 14 15 16 17 18 19 20 21 22 23 24

int _hammingWeight(int value) {
  if (value == 0)
    return 0;
  int weight = 0;
  for (int i = 0; i < value.bitLength; ++i) {
    if (value & (1 << i) != 0)
      ++weight;
  }
  return weight;
}

25 26
class _PointerState {
  _PointerState({ this.result, this.lastPosition });
27 28 29 30 31 32
  HitTestResult result;
  Point lastPosition;
}

typedef void EventListener(sky.Event event);

33
/// A hit test entry used by [FlutterBinding]
34 35
class BindingHitTestEntry extends HitTestEntry {
  const BindingHitTestEntry(HitTestTarget target, this.result) : super(target);
36 37

  /// The result of the hit test
38 39 40
  final HitTestResult result;
}

41
/// The glue between the render tree and the Flutter engine
42
class FlutterBinding extends HitTestTarget {
43

44
  FlutterBinding({ RenderBox root: null, RenderView renderViewOverride }) {
45 46 47 48 49 50 51
    assert(_instance == null);
    _instance = this;

    sky.view.setEventCallback(_handleEvent);

    sky.view.setMetricsChangedCallback(_handleMetricsChanged);
    if (renderViewOverride == null) {
52
      _renderView = new RenderView(child: root);
53 54
      _renderView.attach();
      _renderView.rootConstraints = _createConstraints();
55
      _renderView.scheduleInitialFrame();
56 57 58 59
    } else {
      _renderView = renderViewOverride;
    }
    assert(_renderView != null);
60
    scheduler.addPersistentFrameCallback(beginFrame);
61 62 63 64

    assert(_instance == this);
  }

65
  /// The singleton instance of the binding
66 67
  static FlutterBinding get instance => _instance;
  static FlutterBinding _instance;
68

69
  /// The render tree that's attached to the output surface
70
  RenderView get renderView => _renderView;
71
  RenderView _renderView;
72 73 74 75 76 77 78 79

  ViewConstraints _createConstraints() {
    return new ViewConstraints(size: new Size(sky.view.width, sky.view.height));
  }
  void _handleMetricsChanged() {
    _renderView.rootConstraints = _createConstraints();
  }

80
  /// Pump the rendering pipeline to generate a frame for the given time stamp
81
  void beginFrame(double timeStamp) {
82
    RenderObject.flushLayout();
Hixie's avatar
Hixie committed
83
    _renderView.updateCompositingBits();
84
    RenderObject.flushPaint();
85
    _renderView.compositeFrame();
86 87 88
  }

  final List<EventListener> _eventListeners = new List<EventListener>();
89 90 91 92 93 94

  /// Calls listener for every event that isn't localized to a given view coordinate
  void addEventListener(EventListener listener) => _eventListeners.add(listener);

  /// Stops calling listener for every event that isn't localized to a given view coordinate
  bool removeEventListener(EventListener listener) => _eventListeners.remove(listener);
95 96 97 98 99

  void _handleEvent(sky.Event event) {
    if (event is sky.PointerEvent) {
      _handlePointerEvent(event);
    } else {
100 101
      for (EventListener listener in _eventListeners)
        listener(event);
102 103 104
    }
  }

105
  /// A router that routes all pointer events received from the engine
Adam Barth's avatar
Adam Barth committed
106 107
  final PointerRouter pointerRouter = new PointerRouter();

108 109 110
  /// State for all pointers which are currently down.
  /// We do not track the state of hovering pointers because we need
  /// to hit-test them on each movement.
111
  Map<int, _PointerState> _stateForPointer = new Map<int, _PointerState>();
112

Adam Barth's avatar
Adam Barth committed
113
  void _handlePointerEvent(sky.PointerEvent event) {
114 115
    Point position = new Point(event.x, event.y);

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    _PointerState state = _stateForPointer[event.pointer];
    switch (event.type) {
      case 'pointerdown':
        if (state == null) {
          state = new _PointerState(result: hitTest(position), lastPosition: position);
          _stateForPointer[event.pointer] = state;
        }
        break;
      case 'pointermove':
        if (state == null) {
          // The pointer is hovering, ignore it for now since we don't
          // know what to do with it yet.
          return;
        }
        event.dx = position.x - state.lastPosition.x;
        event.dy = position.y - state.lastPosition.y;
        state.lastPosition = position;
        break;
      case 'pointerup':
      case 'pointercancel':
        if (state == null) {
          // This seems to be a spurious event.  Ignore it.
          return;
        }
        // Only remove the pointer state when the last button has been released.
        if (_hammingWeight(event.buttons) <= 1)
          _stateForPointer.remove(event.pointer);
        break;
144
    }
145
    dispatchEvent(event, state.result);
146 147
  }

148
  /// Determine which [HitTestTarget] objects are located at a given position
149 150 151
  HitTestResult hitTest(Point position) {
    HitTestResult result = new HitTestResult();
    _renderView.hitTest(result, position: position);
152
    result.add(new BindingHitTestEntry(this, result));
153 154 155
    return result;
  }

156
  /// Dispatch the given event to the path of the given hit test result
Adam Barth's avatar
Adam Barth committed
157
  void dispatchEvent(sky.Event event, HitTestResult result) {
158
    assert(result != null);
Adam Barth's avatar
Adam Barth committed
159 160
    for (HitTestEntry entry in result.path)
      entry.target.handleEvent(event, entry);
161 162
  }

Adam Barth's avatar
Adam Barth committed
163 164 165 166 167 168 169
  void handleEvent(sky.Event e, BindingHitTestEntry entry) {
    if (e is sky.PointerEvent) {
      sky.PointerEvent event = e;
      pointerRouter.route(event);
      if (event.type == 'pointerdown')
        GestureArena.instance.close(event.pointer);
    }
170
  }
171
}
172

173 174
/// Prints a textual representation of the entire render tree
void debugDumpRenderTree() {
175
  FlutterBinding.instance.renderView.toStringDeep().split('\n').forEach(print);
176
}