|
| 1 | +// Copyright 2021 Samsung Electronics Co., Ltd. All rights reserved. |
| 2 | +// Copyright 2013 The Flutter Authors. All rights reserved. |
| 3 | +// Use of this source code is governed by a BSD-style license that can be |
| 4 | +// found in the LICENSE file. |
| 5 | + |
| 6 | +// github.com:flutter/flutter.git@02c026b03cd31dd3f867e5faeb7e104cce174c5f |
| 7 | +// packages/flutter/lib/src/rendering/platform_view.dart |
| 8 | +// Imported from above file, the content has not been modified. |
| 9 | + |
| 10 | +part of 'webview_flutter_tizen.dart'; |
| 11 | + |
| 12 | +/// How an embedded platform view behave during hit tests. |
| 13 | +enum PlatformViewHitTestBehavior { |
| 14 | + /// Opaque targets can be hit by hit tests, causing them to both receive |
| 15 | + /// events within their bounds and prevent targets visually behind them from |
| 16 | + /// also receiving events. |
| 17 | + opaque, |
| 18 | + |
| 19 | + /// Translucent targets both receive events within their bounds and permit |
| 20 | + /// targets visually behind them to also receive events. |
| 21 | + translucent, |
| 22 | + |
| 23 | + /// Transparent targets don't receive events within their bounds and permit |
| 24 | + /// targets visually behind them to receive events. |
| 25 | + transparent, |
| 26 | +} |
| 27 | + |
| 28 | +enum _PlatformViewState { |
| 29 | + uninitialized, |
| 30 | + resizing, |
| 31 | + ready, |
| 32 | +} |
| 33 | + |
| 34 | +bool _factoryTypesSetEquals<T>(Set<Factory<T>>? a, Set<Factory<T>>? b) { |
| 35 | + if (a == b) { |
| 36 | + return true; |
| 37 | + } |
| 38 | + if (a == null || b == null) { |
| 39 | + return false; |
| 40 | + } |
| 41 | + return setEquals(_factoriesTypeSet(a), _factoriesTypeSet(b)); |
| 42 | +} |
| 43 | + |
| 44 | +Set<Type> _factoriesTypeSet<T>(Set<Factory<T>> factories) { |
| 45 | + return factories.map<Type>((Factory<T> factory) => factory.type).toSet(); |
| 46 | +} |
| 47 | + |
| 48 | +typedef _HandlePointerEvent = Future<void> Function(PointerEvent event); |
| 49 | + |
| 50 | +// This recognizer constructs gesture recognizers from a set of gesture recognizer factories |
| 51 | +// it was give, adds all of them to a gesture arena team with the _PlatformViewGestureRecognizer |
| 52 | +// as the team captain. |
| 53 | +// As long as the gesture arena is unresolved, the recognizer caches all pointer events. |
| 54 | +// When the team wins, the recognizer sends all the cached pointer events to `_handlePointerEvent`, and |
| 55 | +// sets itself to a "forwarding mode" where it will forward any new pointer event to `_handlePointerEvent`. |
| 56 | +class _PlatformViewGestureRecognizer extends OneSequenceGestureRecognizer { |
| 57 | + _PlatformViewGestureRecognizer( |
| 58 | + _HandlePointerEvent handlePointerEvent, |
| 59 | + this.gestureRecognizerFactories, { |
| 60 | + PointerDeviceKind? kind, |
| 61 | + }) : super(kind: kind) { |
| 62 | + team = GestureArenaTeam()..captain = this; |
| 63 | + _gestureRecognizers = gestureRecognizerFactories.map( |
| 64 | + (Factory<OneSequenceGestureRecognizer> recognizerFactory) { |
| 65 | + final OneSequenceGestureRecognizer gestureRecognizer = |
| 66 | + recognizerFactory.constructor(); |
| 67 | + gestureRecognizer.team = team; |
| 68 | + // The below gesture recognizers requires at least one non-empty callback to |
| 69 | + // compete in the gesture arena. |
| 70 | + // https://github.com/flutter/flutter/issues/35394#issuecomment-562285087 |
| 71 | + if (gestureRecognizer is LongPressGestureRecognizer) { |
| 72 | + gestureRecognizer.onLongPress ??= () {}; |
| 73 | + } else if (gestureRecognizer is DragGestureRecognizer) { |
| 74 | + gestureRecognizer.onDown ??= (_) {}; |
| 75 | + } else if (gestureRecognizer is TapGestureRecognizer) { |
| 76 | + gestureRecognizer.onTapDown ??= (_) {}; |
| 77 | + } |
| 78 | + return gestureRecognizer; |
| 79 | + }, |
| 80 | + ).toSet(); |
| 81 | + _handlePointerEvent = handlePointerEvent; |
| 82 | + } |
| 83 | + |
| 84 | + late _HandlePointerEvent _handlePointerEvent; |
| 85 | + |
| 86 | + // Maps a pointer to a list of its cached pointer events. |
| 87 | + // Before the arena for a pointer is resolved all events are cached here, if we win the arena |
| 88 | + // the cached events are dispatched to `_handlePointerEvent`, if we lose the arena we clear the cache for |
| 89 | + // the pointer. |
| 90 | + final Map<int, List<PointerEvent>> cachedEvents = <int, List<PointerEvent>>{}; |
| 91 | + |
| 92 | + // Pointer for which we have already won the arena, events for pointers in this set are |
| 93 | + // immediately dispatched to `_handlePointerEvent`. |
| 94 | + final Set<int> forwardedPointers = <int>{}; |
| 95 | + |
| 96 | + // We use OneSequenceGestureRecognizers as they support gesture arena teams. |
| 97 | + // TODO(amirh): get a list of GestureRecognizers here. |
| 98 | + // https://github.com/flutter/flutter/issues/20953 |
| 99 | + final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizerFactories; |
| 100 | + late Set<OneSequenceGestureRecognizer> _gestureRecognizers; |
| 101 | + |
| 102 | + @override |
| 103 | + void addAllowedPointer(PointerDownEvent event) { |
| 104 | + startTrackingPointer(event.pointer, event.transform); |
| 105 | + for (final OneSequenceGestureRecognizer recognizer in _gestureRecognizers) { |
| 106 | + recognizer.addPointer(event); |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + @override |
| 111 | + String get debugDescription => 'Platform view'; |
| 112 | + |
| 113 | + @override |
| 114 | + void didStopTrackingLastPointer(int pointer) {} |
| 115 | + |
| 116 | + @override |
| 117 | + void handleEvent(PointerEvent event) { |
| 118 | + if (!forwardedPointers.contains(event.pointer)) { |
| 119 | + _cacheEvent(event); |
| 120 | + } else { |
| 121 | + _handlePointerEvent(event); |
| 122 | + } |
| 123 | + stopTrackingIfPointerNoLongerDown(event); |
| 124 | + } |
| 125 | + |
| 126 | + @override |
| 127 | + void acceptGesture(int pointer) { |
| 128 | + _flushPointerCache(pointer); |
| 129 | + forwardedPointers.add(pointer); |
| 130 | + } |
| 131 | + |
| 132 | + @override |
| 133 | + void rejectGesture(int pointer) { |
| 134 | + stopTrackingPointer(pointer); |
| 135 | + cachedEvents.remove(pointer); |
| 136 | + } |
| 137 | + |
| 138 | + void _cacheEvent(PointerEvent event) { |
| 139 | + if (!cachedEvents.containsKey(event.pointer)) { |
| 140 | + cachedEvents[event.pointer] = <PointerEvent>[]; |
| 141 | + } |
| 142 | + cachedEvents[event.pointer]!.add(event); |
| 143 | + } |
| 144 | + |
| 145 | + void _flushPointerCache(int pointer) { |
| 146 | + cachedEvents.remove(pointer)?.forEach(_handlePointerEvent); |
| 147 | + } |
| 148 | + |
| 149 | + @override |
| 150 | + void stopTrackingPointer(int pointer) { |
| 151 | + super.stopTrackingPointer(pointer); |
| 152 | + forwardedPointers.remove(pointer); |
| 153 | + } |
| 154 | + |
| 155 | + void reset() { |
| 156 | + forwardedPointers.forEach(super.stopTrackingPointer); |
| 157 | + forwardedPointers.clear(); |
| 158 | + cachedEvents.keys.forEach(super.stopTrackingPointer); |
| 159 | + cachedEvents.clear(); |
| 160 | + resolve(GestureDisposition.rejected); |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +/// The Mixin handling the pointer events and gestures of a platform view render box. |
| 165 | +mixin _PlatformViewGestureMixin on RenderBox implements MouseTrackerAnnotation { |
| 166 | + /// How to behave during hit testing. |
| 167 | + // Changing _hitTestBehavior might affect which objects are considered hovered over. |
| 168 | + set hitTestBehavior(PlatformViewHitTestBehavior value) { |
| 169 | + if (value != _hitTestBehavior) { |
| 170 | + _hitTestBehavior = value; |
| 171 | + if (owner != null) markNeedsPaint(); |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + PlatformViewHitTestBehavior? _hitTestBehavior; |
| 176 | + |
| 177 | + _HandlePointerEvent? _handlePointerEvent; |
| 178 | + |
| 179 | + /// Any active gesture arena the `PlatformView` participates in is rejected when the |
| 180 | + /// set of gesture recognizers is changed. |
| 181 | + void _updateGestureRecognizersWithCallBack( |
| 182 | + Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers, |
| 183 | + _HandlePointerEvent handlePointerEvent) { |
| 184 | + assert(gestureRecognizers != null); |
| 185 | + assert( |
| 186 | + _factoriesTypeSet(gestureRecognizers).length == gestureRecognizers.length, |
| 187 | + 'There were multiple gesture recognizer factories for the same type, there must only be a single ' |
| 188 | + 'gesture recognizer factory for each gesture recognizer type.', |
| 189 | + ); |
| 190 | + if (_factoryTypesSetEquals( |
| 191 | + gestureRecognizers, _gestureRecognizer?.gestureRecognizerFactories)) { |
| 192 | + return; |
| 193 | + } |
| 194 | + _gestureRecognizer?.dispose(); |
| 195 | + _gestureRecognizer = |
| 196 | + _PlatformViewGestureRecognizer(handlePointerEvent, gestureRecognizers); |
| 197 | + _handlePointerEvent = handlePointerEvent; |
| 198 | + } |
| 199 | + |
| 200 | + _PlatformViewGestureRecognizer? _gestureRecognizer; |
| 201 | + |
| 202 | + @override |
| 203 | + bool hitTest(BoxHitTestResult result, {required Offset position}) { |
| 204 | + if (_hitTestBehavior == PlatformViewHitTestBehavior.transparent || |
| 205 | + !size.contains(position)) { |
| 206 | + return false; |
| 207 | + } |
| 208 | + result.add(BoxHitTestEntry(this, position)); |
| 209 | + return _hitTestBehavior == PlatformViewHitTestBehavior.opaque; |
| 210 | + } |
| 211 | + |
| 212 | + @override |
| 213 | + bool hitTestSelf(Offset position) => |
| 214 | + _hitTestBehavior != PlatformViewHitTestBehavior.transparent; |
| 215 | + |
| 216 | + @override |
| 217 | + PointerEnterEventListener? get onEnter => null; |
| 218 | + |
| 219 | + @override |
| 220 | + PointerExitEventListener? get onExit => null; |
| 221 | + |
| 222 | + @override |
| 223 | + MouseCursor get cursor => MouseCursor.uncontrolled; |
| 224 | + |
| 225 | + @override |
| 226 | + bool get validForMouseTracker => true; |
| 227 | + |
| 228 | + @override |
| 229 | + void handleEvent(PointerEvent event, HitTestEntry entry) { |
| 230 | + if (event is PointerDownEvent) { |
| 231 | + _gestureRecognizer!.addPointer(event); |
| 232 | + } |
| 233 | + if (event is PointerHoverEvent) { |
| 234 | + _handlePointerEvent?.call(event); |
| 235 | + } |
| 236 | + } |
| 237 | + |
| 238 | + @override |
| 239 | + void detach() { |
| 240 | + _gestureRecognizer!.reset(); |
| 241 | + super.detach(); |
| 242 | + } |
| 243 | +} |
0 commit comments