sky_binding.dart 5.29 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;

Adam Barth's avatar
Adam Barth committed
7
import 'package:sky/base/pointer_router.dart';
8
import 'package:sky/base/hit_test.dart';
9
import 'package:sky/base/scheduler.dart' as scheduler;
10
import 'package:sky/gestures/arena.dart';
11 12 13
import 'package:sky/src/rendering/box.dart';
import 'package:sky/src/rendering/object.dart';
import 'package:sky/src/rendering/view.dart';
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

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;
}

class PointerState {
  PointerState({ this.result, this.lastPosition });
  HitTestResult result;
  Point lastPosition;
}

typedef void EventListener(sky.Event event);

34 35 36 37 38 39
class BindingHitTestEntry extends HitTestEntry {
  const BindingHitTestEntry(HitTestTarget target, this.result) : super(target);
  final HitTestResult result;
}

class SkyBinding extends HitTestTarget {
40 41 42 43 44 45 46 47 48 49

  SkyBinding({ RenderBox root: null, RenderView renderViewOverride }) {
    assert(_instance == null);
    _instance = this;

    sky.view.setEventCallback(_handleEvent);

    sky.view.setMetricsChangedCallback(_handleMetricsChanged);
    scheduler.init();
    if (renderViewOverride == null) {
50
      _renderView = new RenderView(child: root);
51 52
      _renderView.attach();
      _renderView.rootConstraints = _createConstraints();
53
      _renderView.scheduleInitialFrame();
54 55 56 57
    } else {
      _renderView = renderViewOverride;
    }
    assert(_renderView != null);
58
    scheduler.addPersistentFrameCallback(beginFrame);
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

    assert(_instance == this);
  }

  static SkyBinding _instance; // used to enforce that we're a singleton
  static SkyBinding get instance => _instance;

  RenderView _renderView;
  RenderView get renderView => _renderView;

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

  RenderBox get root => _renderView.child;
  void set root(RenderBox value) {
    _renderView.child = value;
  }
80
  void beginFrame(double timeStamp) {
81
    RenderObject.flushLayout();
Hixie's avatar
Hixie committed
82
    _renderView.updateCompositingBits();
83
    RenderObject.flushPaint();
84
    _renderView.compositeFrame();
85 86 87 88 89 90 91 92 93 94
  }

  final List<EventListener> _eventListeners = new List<EventListener>();
  void addEventListener(EventListener e) => _eventListeners.add(e);
  bool removeEventListener(EventListener e) => _eventListeners.remove(e);

  void _handleEvent(sky.Event event) {
    if (event is sky.PointerEvent) {
      _handlePointerEvent(event);
    } else if (event is sky.GestureEvent) {
95
      dispatchEvent(event, hitTest(new Point(event.x, event.y)));
96
    } else {
97 98
      for (EventListener listener in _eventListeners)
        listener(event);
99 100 101
    }
  }

Adam Barth's avatar
Adam Barth committed
102 103
  final PointerRouter pointerRouter = new PointerRouter();

104 105 106
  Map<int, PointerState> _stateForPointer = new Map<int, PointerState>();

  PointerState _createStateForPointer(sky.PointerEvent event, Point position) {
107
    HitTestResult result = hitTest(position);
108 109 110 111 112
    PointerState state = new PointerState(result: result, lastPosition: position);
    _stateForPointer[event.pointer] = state;
    return state;
  }

113 114 115 116 117 118 119
  PointerState _getOrCreateStateForPointer(event, position) {
    PointerState state = _stateForPointer[event.pointer];
    if (state == null)
      state = _createStateForPointer(event, position);
    return state;
  }

120
  EventDisposition _handlePointerEvent(sky.PointerEvent event) {
121 122
    Point position = new Point(event.x, event.y);

123 124 125 126 127
    PointerState state = _getOrCreateStateForPointer(event, position);

    if (event.type == 'pointerup' || event.type == 'pointercancel') {
      if (_hammingWeight(event.buttons) <= 1)
        _stateForPointer.remove(event.pointer);
128
    }
129

130 131 132 133
    event.dx = position.x - state.lastPosition.x;
    event.dy = position.y - state.lastPosition.y;
    state.lastPosition = position;

134
    return dispatchEvent(event, state.result);
135 136
  }

137 138
  HitTestResult hitTest(Point position) {
    HitTestResult result = new HitTestResult();
139
    result.add(new BindingHitTestEntry(this, result));
140 141 142 143
    _renderView.hitTest(result, position: position);
    return result;
  }

144
  EventDisposition dispatchEvent(sky.Event event, HitTestResult result) {
145
    assert(result != null);
146 147 148 149 150 151 152 153 154
    EventDisposition disposition = EventDisposition.ignored;
    for (HitTestEntry entry in result.path.reversed) {
      EventDisposition entryDisposition = entry.target.handleEvent(event, entry);
      if (entryDisposition == EventDisposition.consumed)
        return EventDisposition.consumed;
      else if (entryDisposition == EventDisposition.processed)
        disposition = EventDisposition.processed;
    }
    return disposition;
155 156
  }

157 158 159 160 161 162 163 164 165 166
  EventDisposition handleEvent(sky.Event e, BindingHitTestEntry entry) {
    if (e is! sky.PointerEvent)
      return EventDisposition.ignored;
    sky.PointerEvent event = e;
    pointerRouter.route(event);
    if (event.type == 'pointerdown')
      GestureArena.instance.close(event.pointer);
    return EventDisposition.processed;
  }

167 168 169 170 171 172 173
  String toString() => 'Render Tree:\n${_renderView}';

  void debugDumpRenderTree() {
    toString().split('\n').forEach(print);
  }

}