-
Notifications
You must be signed in to change notification settings - Fork 28.5k
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
base: master
Are you sure you want to change the base?
Conversation
49fc431
to
c145704
Compare
e0035e1
to
18498b3
Compare
This is ready for a review, curious to hear thoughts on the |
There was a problem hiding this 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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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():
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
…by line on triple click + drag on Linux platform
09194c7
to
a5b3dd6
Compare
/// 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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 Selectable
s. Some Selectable
s 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
@Renzo-Olivares Heads up you have merge conflicts. |
There was a problem hiding this 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. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 says this is still on his radar to return to in the future. |
Hey @Renzo-Olivares this one came up again in stale PR triage - is it still something you'd like to work on? |
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
///
).