Skip to content

Commit

Permalink
Allow multiple tap handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
angelosilvestre committed Nov 18, 2024
1 parent 7ae0cdc commit 5e89455
Show file tree
Hide file tree
Showing 8 changed files with 389 additions and 308 deletions.
129 changes: 83 additions & 46 deletions super_editor/lib/src/default_editor/document_gestures_mouse.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
Expand Down Expand Up @@ -49,7 +50,7 @@ class DocumentMouseInteractor extends StatefulWidget {
required this.getDocumentLayout,
required this.selectionNotifier,
required this.selectionChanges,
this.contentTapHandler,
this.contentTapHandlers,
required this.autoScroller,
required this.fillViewport,
this.showDebugPaint = false,
Expand All @@ -64,9 +65,12 @@ class DocumentMouseInteractor extends StatefulWidget {
final Stream<DocumentSelectionChange> selectionChanges;
final ValueListenable<DocumentSelection?> selectionNotifier;

/// Optional handler that responds to taps on content, e.g., opening
/// Optional list of handlers that respond to taps on content, e.g., opening
/// a link when the user taps on text with a link attribution.
final ContentTapDelegate? contentTapHandler;
///
/// If a handler returns [TapHandlingInstruction.halt], no subsequent handlers
/// nor the default tap behavior will be executed.
final List<ContentTapDelegate>? contentTapHandlers;

/// Auto-scrolling delegate.
final AutoScrollController autoScroller;
Expand Down Expand Up @@ -124,7 +128,12 @@ class _DocumentMouseInteractorState extends State<DocumentMouseInteractor> with
widget.autoScroller
..addListener(_updateDragSelection)
..addListener(_updateMouseCursorAtLatestOffset);
widget.contentTapHandler?.addListener(_updateMouseCursorAtLatestOffset);

if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
handler.addListener(_updateMouseCursorAtLatestOffset);
}
}
}

@override
Expand All @@ -148,15 +157,28 @@ class _DocumentMouseInteractorState extends State<DocumentMouseInteractor> with
..addListener(_updateDragSelection)
..addListener(_updateMouseCursorAtLatestOffset);
}
if (widget.contentTapHandler != oldWidget.contentTapHandler) {
oldWidget.contentTapHandler?.removeListener(_updateMouseCursorAtLatestOffset);
widget.contentTapHandler?.addListener(_updateMouseCursorAtLatestOffset);
if (!const DeepCollectionEquality().equals(oldWidget.contentTapHandlers, widget.contentTapHandlers)) {
if (oldWidget.contentTapHandlers != null) {
for (final handler in oldWidget.contentTapHandlers!) {
handler.removeListener(_updateMouseCursorAtLatestOffset);
}
}

if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
handler.addListener(_updateMouseCursorAtLatestOffset);
}
}
}
}

@override
void dispose() {
widget.contentTapHandler?.removeListener(_updateMouseCursorAtLatestOffset);
if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
handler.removeListener(_updateMouseCursorAtLatestOffset);
}
}
if (widget.focusNode == null) {
_focusNode.dispose();
}
Expand Down Expand Up @@ -253,18 +275,20 @@ class _DocumentMouseInteractorState extends State<DocumentMouseInteractor> with

_focusNode.requestFocus();

if (widget.contentTapHandler != null) {
final result = widget.contentTapHandler!.onTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
final result = handler.onTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
}
}
}

Expand Down Expand Up @@ -314,18 +338,20 @@ class _DocumentMouseInteractorState extends State<DocumentMouseInteractor> with
final docOffset = _getDocOffsetFromGlobalOffset(details.globalPosition);
editorGesturesLog.fine(" - document offset: $docOffset");

if (widget.contentTapHandler != null) {
final result = widget.contentTapHandler!.onDoubleTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
final result = handler.onDoubleTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
}
}
}

Expand Down Expand Up @@ -421,18 +447,20 @@ class _DocumentMouseInteractorState extends State<DocumentMouseInteractor> with
final docOffset = _getDocOffsetFromGlobalOffset(details.globalPosition);
editorGesturesLog.fine(" - document offset: $docOffset");

if (widget.contentTapHandler != null) {
final result = widget.contentTapHandler!.onTripleTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
final result = handler.onTripleTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
}
}
}

Expand Down Expand Up @@ -733,8 +761,17 @@ Updating drag selection:
return;
}

final cursorForContent = widget.contentTapHandler?.mouseCursorForContentHover(docPosition);
_mouseCursor.value = cursorForContent ?? SystemMouseCursors.text;
if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
final cursorForContent = handler.mouseCursorForContentHover(docPosition);
if (cursorForContent != null) {
_mouseCursor.value = cursorForContent;
return;
}
}
}

_mouseCursor.value = SystemMouseCursors.text;
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ class AndroidDocumentTouchInteractor extends StatefulWidget {
required this.openSoftwareKeyboard,
required this.scrollController,
required this.fillViewport,
this.contentTapHandler,
this.contentTapHandlers,
this.dragAutoScrollBoundary = const AxisOffset.symmetric(54),
required this.dragHandleAutoScroller,
this.showDebugPaint = false,
Expand All @@ -425,9 +425,12 @@ class AndroidDocumentTouchInteractor extends StatefulWidget {
/// A callback that should open the software keyboard when invoked.
final VoidCallback openSoftwareKeyboard;

/// Optional handler that responds to taps on content, e.g., opening
/// Optional list of handlers that respond to taps on content, e.g., opening
/// a link when the user taps on text with a link attribution.
final ContentTapDelegate? contentTapHandler;
///
/// If a handler returns [TapHandlingInstruction.halt], no subsequent handlers
/// nor the default tap behavior will be executed.
final List<ContentTapDelegate>? contentTapHandlers;

final ScrollController scrollController;

Expand Down Expand Up @@ -729,18 +732,20 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt
final docOffset = _getDocumentOffsetFromGlobalOffset(details.globalPosition);
editorGesturesLog.fine(" - document offset: $docOffset");

if (widget.contentTapHandler != null) {
final result = widget.contentTapHandler!.onTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
final result = handler.onTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
}
}
}

Expand Down Expand Up @@ -794,18 +799,20 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt
final docOffset = _getDocumentOffsetFromGlobalOffset(details.globalPosition);
editorGesturesLog.fine(" - document offset: $docOffset");

if (widget.contentTapHandler != null) {
final result = widget.contentTapHandler!.onDoubleTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
final result = handler.onDoubleTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
}
}
}

Expand Down Expand Up @@ -875,18 +882,20 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt
final docOffset = _getDocumentOffsetFromGlobalOffset(details.globalPosition);
editorGesturesLog.fine(" - document offset: $docOffset");

if (widget.contentTapHandler != null) {
final result = widget.contentTapHandler!.onTripleTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
if (widget.contentTapHandlers != null) {
for (final handler in widget.contentTapHandlers!) {
final result = handler.onTripleTap(
DocumentTapDetails(
documentLayout: _docLayout,
layoutOffset: docOffset,
globalOffset: details.globalPosition,
),
);
if (result == TapHandlingInstruction.halt) {
// The custom tap handler doesn't want us to react at all
// to the tap.
return;
}
}
}

Expand Down
Loading

0 comments on commit 5e89455

Please sign in to comment.