singerdmx / flutter-quill

Rich text editor for Flutter
https://pub.dev/packages/flutter_quill
MIT License
2.6k stars 838 forks source link

Long Tap Selection Doesn't Trigger Selection (also Selection Handles) in iOS #2391

Open ataberkw opened 16 hours ago

ataberkw commented 16 hours ago

Is there an existing issue for this?

Flutter Quill version

10.8.5, also 11.0.0-dev.13

Steps to reproduce

Build a basic QuillEditor. Then build it on iOS, double tap to select works good, also triggers handles. But long tap to select will only show magnifier and copy, select all options after you end selecting. Same behavior in iPad. Mouse selection works good on iPad.

This is how my QuillEditor looks like:

      final json = jsonDecode(widget.data) as Json;
      final ops = (json['ops'] as List<dynamic>).map((e) => e as Json).toList();
      final operations =
          ops.map(Operation.fromJson).map(QuillUtils.mapColor).toList();

      _controller = QuillController(
        document: Document.fromDelta(Delta.fromOperations(operations)),
        selection: const TextSelection.collapsed(offset: 0),
        readOnly: true,
      );

DefaultTextStyle(
      style: GlobalUIData.defaultStyle!.copyWith(color: widget.textColor),
      child: QuillEditor(
        focusNode: FocusNode(),
        controller: _controller!,
        scrollController: ScrollController(),
        config: QuillEditorConfig(
          textSelectionControls: materialTextSelectionControls,
          textSelectionThemeData: TextSelectionThemeData(
            cursorColor: textSelectionTheme2.cursorColor,
            selectionColor: textSelectionTheme2.selectionColor,
            selectionHandleColor: textSelectionTheme2.selectionHandleColor,
          ),
          enableInteractiveSelection: true,
          showCursor: false,
          autoFocus: false,
          embedBuilders: [
            CustomFormulaEmbedBuilder(widget.textColor),
            CustomImageEmbedBuilder(onImageTap: widget.onImageTap),
            VideoEmbedBuilder(onVideoInit: null),
            CodeHighlightingEmbedBuilder(),
          ],
          expands: false,
        ),
      ),
    )

Expected results

Everything is good on Android.

https://github.com/user-attachments/assets/b6b854e0-9270-4d8b-a7c1-ee22c12166e3

Actual results

I double tap at 00:15

https://github.com/user-attachments/assets/be697a83-705a-44c3-be2a-17f55ed6936b

Additional Context

When I long tap to select, it selects the text as collapsed

TextSelection.collapsed(offset: 244, affinity: TextAffinity.upstream, isDirectional: false)

EchoEllet commented 16 hours ago

Can you confirm if this issue exists on older versions (before the magnifier feature #2026)?

flutter_quill: 9.5.23
ataberkw commented 15 hours ago

As I checked the code, this is because when device is iOS the library coded to not select in range when long tapped. It just pops up magnifier. But at the end of selection, it asks to copy or select all. Which doesn't seem like a good flow.

I checked Cupertino behavior in other iOS apps. Long tap to select shows magnifier and also select the text in range.

lib/src/editor/editor.dart:

  @override
  void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
    if (_state.configurations.onSingleLongTapMoveUpdate != null) {
      if (renderEditor != null &&
          _state.configurations.onSingleLongTapMoveUpdate!(
            details,
            renderEditor!.getPositionForOffset,
          )) {
        return;
      }
    }
    if (!delegate.selectionEnabled) {
      return;
    }

   //  bool get isCupertino =>  {TargetPlatform.iOS, TargetPlatform.macOS}.contains(platform);
    if (Theme.of(_state.context).isCupertino) { // <== When device is iOS, this can not be changed.
      renderEditor!.selectPositionAt(
        from: details.globalPosition,
        cause: SelectionChangedCause.longPress,
      );
    } else {
      renderEditor!.selectWordsInRange(
        details.globalPosition - details.offsetFromOrigin,
        details.globalPosition,
        SelectionChangedCause.longPress,
      );
    }
    editor?.updateMagnifier(details.globalPosition);
  }

A solution may be adding a parameter to decide the long tap select whether ranged or collapsed. Or maybe whatever the better for Cupertino behavior.

ataberkw commented 15 hours ago

Can you confirm if this issue exists on older versions (before the magnifier feature #2026)?

flutter_quill: 9.5.23

My project is not compatible with older intl. So I couldn't directly test it. But it seems like the problem still was there on that version. Because it still selects position on long tap if it is iOS. Instead, i think it should select range. Am I wrong?


  @override
  void onSingleLongTapStart(LongPressStartDetails details) {
    if (_state.configurations.onSingleLongTapStart != null) {
      if (renderEditor != null &&
          _state.configurations.onSingleLongTapStart!(
            details,
            renderEditor!.getPositionForOffset,
          )) {
        return;
      }
    }

    if (delegate.selectionEnabled) {
      if (Theme.of(_state.context).isCupertino) {
        renderEditor!.selectPositionAt(
          from: details.globalPosition,
          cause: SelectionChangedCause.longPress,
        );
      } else {
        renderEditor!.selectWord(SelectionChangedCause.longPress);
        Feedback.forLongPress(_state.context);
      }
    }

    _showMagnifierIfSupported(details.globalPosition);
  }```
EchoEllet commented 14 hours ago

if (Theme.of(_state.context).isCupertino)

I'm not sure about this check either, it seems this is a platform-specific check that allows the use of Theme which allows overriding the platform, leading to broken behavior, it probably should use isIOSApp instead which will be only true if running on an iOS mobile app (not browser or other platforms that uses Cupertino). I didn't change it to avoid regressions.

My project is not compatible with older intl. So I couldn't directly test it.

Use pub dependency overrides:

dependency_overrides:
  flutter_quill: 9.5.23

Or try the example with version 9.4.0:

$ git clone --depth 1 --branch v9.4.0 https://github.com/singerdmx/flutter-quill.git
$ (cd flutter-quill/example && flutter run)

If you confirm this is not an issue on older versions, we will have enough regressions to revert the magnifier feature in v11 (breaking change). We're still discussing this change though.