binding.dart 4.59 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1 2 3 4 5
// 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:typed_data';
6
import 'dart:ui' as ui show window;
Ian Hickson's avatar
Ian Hickson committed
7 8 9 10 11 12 13 14 15 16 17 18

import 'package:flutter/services.dart';
import 'package:mojo/bindings.dart' as mojo_bindings;
import 'package:mojo/core.dart' as mojo_core;
import 'package:sky_services/pointer/pointer.mojom.dart';

import 'arena.dart';
import 'converter.dart';
import 'events.dart';
import 'hit_test.dart';
import 'pointer_router.dart';

19 20
typedef void GesturerExceptionHandler(PointerEvent event, HitTestTarget target, dynamic exception, StackTrace stack);

21
/// A binding for the gesture subsystem.
22
abstract class Gesturer extends BindingBase implements HitTestTarget, HitTestable {
Ian Hickson's avatar
Ian Hickson committed
23 24 25 26 27 28 29

  void initInstances() {
    super.initInstances();
    _instance = this;
    ui.window.onPointerPacket = _handlePointerPacket;
  }

30
  /// The singleton instance of this object.
31
  static Gesturer get instance => _instance;
32
  static Gesturer _instance;
Ian Hickson's avatar
Ian Hickson committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

  void _handlePointerPacket(ByteData serializedPacket) {
    final mojo_bindings.Message message = new mojo_bindings.Message(
      serializedPacket,
      <mojo_core.MojoHandle>[],
      serializedPacket.lengthInBytes,
      0
    );
    final PointerPacket packet = PointerPacket.deserialize(message);
    for (PointerEvent event in PointerEventConverter.expand(packet.pointers))
      _handlePointerEvent(event);
  }

  /// A router that routes all pointer events received from the engine.
  final PointerRouter pointerRouter = new PointerRouter();

  /// The gesture arenas used for disambiguating the meaning of sequences of
  /// pointer events.
  final GestureArena gestureArena = new GestureArena();

  /// State for all pointers which are currently down.
  ///
  /// The state of hovering pointers is not tracked because that would require
  /// hit-testing on every frame.
  Map<int, HitTestResult> _hitTests = <int, HitTestResult>{};

  void _handlePointerEvent(PointerEvent event) {
    if (event is PointerDownEvent) {
      assert(!_hitTests.containsKey(event.pointer));
      HitTestResult result = new HitTestResult();
      hitTest(result, event.position);
      _hitTests[event.pointer] = result;
    } else if (event is! PointerUpEvent) {
      assert(event.down == _hitTests.containsKey(event.pointer));
      if (!event.down)
        return; // we currently ignore add, remove, and hover move events
    }
    assert(_hitTests[event.pointer] != null);
    dispatchEvent(event, _hitTests[event.pointer]);
    if (event is PointerUpEvent) {
      assert(_hitTests.containsKey(event.pointer));
      _hitTests.remove(event.pointer);
    }
  }

  /// Determine which [HitTestTarget] objects are located at a given position.
  void hitTest(HitTestResult result, Point position) {
    result.add(new HitTestEntry(this));
  }

83 84 85 86
  /// This callback is invoked whenever an exception is caught by the Gesturer
  /// binding. The 'event' argument is the pointer event that was being routed.
  /// The 'target' argument is the class whose handleEvent function threw the
  /// exception. The 'exception' argument contains the object that was thrown,
87 88
  /// and the 'stack' argument contains the stack trace. If no handler is
  /// registered, then the information will be printed to the console instead.
89 90
  GesturerExceptionHandler debugGesturerExceptionHandler;

Ian Hickson's avatar
Ian Hickson committed
91 92 93
  /// Dispatch the given event to the path of the given hit test result
  void dispatchEvent(PointerEvent event, HitTestResult result) {
    assert(result != null);
94 95 96 97
    for (HitTestEntry entry in result.path) {
      try {
        entry.target.handleEvent(event, entry);
      } catch (exception, stack) {
98
        if (debugGesturerExceptionHandler != null) {
99
          debugGesturerExceptionHandler(event, entry.target, exception, stack);
100 101 102 103 104 105 106 107 108 109 110 111
        } else {
          debugPrint('-- EXCEPTION CAUGHT BY GESTURE LIBRARY ---------------------------------');
          debugPrint('The following exception was raised while dispatching a pointer event:');
          debugPrint('$exception');
          debugPrint('Event:');
          debugPrint('$event');
          debugPrint('Target:');
          debugPrint('${entry.target}');
          debugPrint('Stack trace:');
          debugPrint('$stack');
          debugPrint('------------------------------------------------------------------------');
        }
112 113
      }
    }
Ian Hickson's avatar
Ian Hickson committed
114 115 116 117 118 119 120 121 122 123 124
  }

  void handleEvent(PointerEvent event, HitTestEntry entry) {
    pointerRouter.route(event);
    if (event is PointerDownEvent) {
      gestureArena.close(event.pointer);
    } else if (event is PointerUpEvent) {
      gestureArena.sweep(event.pointer);
    }
  }
}