Thanks to visit codestin.com
Credit goes to github.com

Skip to content

SelectionArea support for triple click/tap to select line on Linux #153514

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

Renzo-Olivares
Copy link
Contributor

@Renzo-Olivares Renzo-Olivares commented Aug 15, 2024

This change adds support for triple click/tap to select a line at the location on Linux. Also adds support for extending selection by line on triple click followed by a drag.

Fixes: #129583

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@github-actions github-actions bot added a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. f: scrolling Viewports, list views, slivers, etc. d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos labels Aug 15, 2024
@Renzo-Olivares Renzo-Olivares force-pushed the sr-extendbyline branch 2 times, most recently from e0035e1 to 18498b3 Compare September 25, 2024 20:44
@Renzo-Olivares Renzo-Olivares marked this pull request as ready for review September 25, 2024 20:45
@Renzo-Olivares
Copy link
Contributor Author

This is ready for a review, curious to hear thoughts on the SelectBoundarySelectionEvent and SelectMultiSelectableBoundarySelectionEvent.

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍 Code duplication with editable text doesn't look too bad.

case TextGranularity.line:
assert(false, 'Moving the selection edge by line or document is not supported.');
result = _updateSelectionEdgeByMultiSelectableTextBoundary(edgeUpdate.globalPosition, isEnd: edgeUpdate.type == SelectionEventType.endEdgeUpdate, getTextBoundary: _getLineBoundaryAtPosition, getClampedTextBoundary: _getClampedLineBoundaryAtPosition);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Break this onto multiple lines to make it more readable?

),
);
final RenderParagraph paragraph = tester.renderObject<RenderParagraph>(find.descendant(of: find.text(longText), matching: find.byType(RichText)));
final TestGesture gesture = await tester.startGesture(textOffsetToPosition(paragraph, 2), kind: PointerDeviceKind.mouse);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Maybe comment that this is a triple click (like you did below).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And in a few other places too.


await gesture.up();
}, variant: TargetPlatformVariant.only(TargetPlatform.linux),
skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Closing parenthesis on the next line (here and elsewhere).

@@ -1441,6 +1442,15 @@ class _SelectableFragment with Selectable, Diagnosticable, ChangeNotifier implem
} else {
result = _handleSelectParagraph(selectParagraph.globalPosition);
}
case SelectionEventType.selectLine:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it is possible to use pattern match like

case SelectLineSelectionEvent event so that we no longer rely on the event type and downward cast

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works! Any thoughts on binding to a variable vs not?

case SelectLineSelectionEvent event: vs case SelectLineSelectionEvent():

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure, but from the looks of it, it seems the later will create a new object which may not be necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think either creates a new object, the latter just requires that syntax for pattern matching. The former creates a reference to the original object from what I understand.

case TextGranularity.line:
assert(false, 'Moving the selection edge by line or document is not supported.');
result = _updateSelectionEdgeByMultiSelectableTextBoundary(edgeUpdate.globalPosition, isEnd: edgeUpdate.type == SelectionEventType.endEdgeUpdate, getTextBoundary: _getLineBoundaryAtPosition, getClampedTextBoundary: _getClampedLineBoundaryAtPosition);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the indent for this _updateSelectionEdgeByMultiSelectableTextBoundary declaration is off.

  SelectionResult _updateSelectionEdgeByMultiSelectableTextBoundary(
    Offset globalPosition, {
    required bool isEnd,
    required _TextBoundaryAtPositionInText getTextBoundary,
    required _TextBoundaryAtPosition getClampedTextBoundary,
  }) {

const SelectMultiSelectableBoundarySelectionEvent._(this.absorb, Offset globalPosition, SelectionEventType type) : super._(globalPosition, type);

/// Whether the selectable receiving the event should be absorbed into
/// an encompassing boundary.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain a bit more in the doc? what is encompassing boundary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some more docs in bf5d04c (#153514).

@@ -1441,6 +1442,15 @@ class _SelectableFragment with Selectable, Diagnosticable, ChangeNotifier implem
} else {
result = _handleSelectParagraph(selectParagraph.globalPosition);
}
case SelectionEventType.selectLine:
final SelectLineSelectionEvent selectLine = event as SelectLineSelectionEvent;
if (selectLine.absorb) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain a bit what absorb is and how is it use?

Copy link
Contributor Author

@Renzo-Olivares Renzo-Olivares Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added absorb to support cases like below:

| Start of paragraph | | inline element | | End of paragraph |

If we click somewhere on the Start of paragraph or End of paragraph it should select all content in the inline element. This is consistent with the behavior on web and with our SelectableText.

Screen.Recording.2024-10-01.at.11.43.38.AM.mov
Screen.Recording.2024-10-01.at.11.47.42.AM.mov

When a Selectable receives this event set to true it should select all of its contents, and be absorbed into the boundary. When it is false it should process the event as normal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case should the parent selectable container send a select all event directly to the inline element?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think with a select all event the child won't be able to differentiate that its selection is part of an encompassing boundary. Right now It uses this information maintain its selection as part of the origin of the selection

if (_selectableContainsOriginTextBoundary && existingSelectionStart != null && existingSelectionEnd != null) {
.

/// When [absorb] is set to false, and a [Selectable] that is inline with a boundary
/// receives this event, it should find the end of the boundary relative to itself
/// and stop searching.
final bool absorb;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on the doc and the code, can we replace case with absorb = true with a select all event?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answered in: #153514 (comment)

/// Whether the [Selectable] receiving the event should be completely
/// absorbed into a boundary.
///
/// For example, when [absorb] is set to true, and a [Selectable] that is inline
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is inline within a boundary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I said "inline" I was thinking about text. I think just a [Selectable] within a boundary is more clear.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here inline within any piece of text doesn't really help me understand

const SelectMultiSelectableBoundarySelectionEvent._(this.absorb, Offset globalPosition, SelectionEventType type) : super._(globalPosition, type);

/// Whether the [Selectable] receiving the event should be completely
/// absorbed into a boundary.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't parse this sentence either.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it feel to me the boundary word has other meaning in this doc, can you explain a bit more?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By boundary I meant a range within some content with a start and end. This boundary can be across multiple Selectables. Some Selectables within a boundary should be able to be considered as part of that boundary as a single unit rather than granularly. Let me know if I can explain more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does that mean any piece of text like TextBoundary? It is not very useful in this doc context.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant reading this doc as a developer who implement Selecable subclass. I still won't know how to deal with this boolean. Some example or a better name would be better.

@chunhtai chunhtai self-requested a review November 12, 2024 23:15
@justinmc
Copy link
Contributor

@Renzo-Olivares Heads up you have merge conflicts.

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgot to submit my last round of review. I remember we have a discussion offline that suggest some other way to avoid the boolean, is this pr ready for another look? @Renzo-Olivares

const SelectMultiSelectableBoundarySelectionEvent._(this.absorb, Offset globalPosition, SelectionEventType type) : super._(globalPosition, type);

/// Whether the [Selectable] receiving the event should be completely
/// absorbed into a boundary.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does that mean any piece of text like TextBoundary? It is not very useful in this doc context.

const SelectMultiSelectableBoundarySelectionEvent._(this.absorb, Offset globalPosition, SelectionEventType type) : super._(globalPosition, type);

/// Whether the [Selectable] receiving the event should be completely
/// absorbed into a boundary.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant reading this doc as a developer who implement Selecable subclass. I still won't know how to deal with this boolean. Some example or a better name would be better.

/// Whether the [Selectable] receiving the event should be completely
/// absorbed into a boundary.
///
/// For example, when [absorb] is set to true, and a [Selectable] that is inline
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here inline within any piece of text doesn't really help me understand

@Renzo-Olivares Renzo-Olivares marked this pull request as draft December 9, 2024 20:00
@Piinks
Copy link
Contributor

Piinks commented Feb 26, 2025

@Renzo-Olivares says this is still on his radar to return to in the future.

@Piinks
Copy link
Contributor

Piinks commented Apr 30, 2025

Hey @Renzo-Olivares this one came up again in stale PR triage - is it still something you'd like to work on?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: text input Entering text in a text field or keyboard related problems d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SelectionArea supported gestures should have feature parity with TextSelectionGestureDetector
4 participants