Skip to content

Conversation

@Renzo-Olivares
Copy link
Contributor

@Renzo-Olivares Renzo-Olivares commented Apr 19, 2024

This change makes _SelectableRegionContainerDelegate public so it can be reused and extended by users of SelectionContainer. Extending MultiSelectableRegionContainerDelegate does not by default provide selection managing across multiple selectables, so often users will copy the implementation found in _SelectableRegionContainerDelegate.

_SelectableRegionContainerDelegate -> StaticSelectionContainerDelegate.

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.

@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact "@test-exemption-reviewer" in the #hackers channel in Chat (don't just cc them here, they won't see it! Use Discord!).

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@github-actions github-actions bot added the framework flutter/packages/flutter repository. See also f: labels. label Apr 19, 2024
@Renzo-Olivares Renzo-Olivares requested a review from chunhtai April 19, 2024 19:04
@github-actions github-actions bot added the a: text input Entering text in a text field or keyboard related problems label Apr 22, 2024
@Renzo-Olivares Renzo-Olivares force-pushed the public-selectioncontainerdelegate branch from a365067 to 32aa4fd Compare April 22, 2024 06:51
@Renzo-Olivares
Copy link
Contributor Author

Friendly bump for review. Also if this needs more tests let me know, I can add them (maybe a separate test file?) but initially didn't because the functionality of the delegate is verified in selectable_region_test.dart.

Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably start with something like

A delegate that manages ...

Copy link
Contributor

Choose a reason for hiding this comment

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

why would this become public? I think the synthesize selection events logic is implementation details.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah this is for subclass to implement their selection logic. It would be good if we can provide some sort of low level selection API, for example

void updateInternalStateBeforeAndAfterSelection(child, callback) {
  ensureChildUpdated(child);
  callback();
   _hasReceivedStartEvent.add(child);
   _hasReceivedEndEvent.add(child);
}

and then subclass can do

SelectionResult handleSelectParagraph(event) {
 for(..) {
    updateInternalStateBeforeAndAfterSelection(selectable,() {_handleSelectParagraph} )
 }
}

If we can't completely hide these internal state, at least make them readonly, so that only one class is updating the state.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think i missed this, why do we need to duplicate these two method in this class?

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 did this to handle the behavior where to keep the origin boundary within the selection we swap the selection edges when they invert. We didn't need this for word boundary because a word boundary is guaranteed to be within one _SelectableFragment, but with paragraphs the origin boundary may span multiple _SelectableFragments so I needed to account for these swaps at the container level so it has an accurate currentSelectionStartIndex and currentSelectionEndIndex.

@Renzo-Olivares Renzo-Olivares force-pushed the public-selectioncontainerdelegate branch 2 times, most recently from 962befb to 24f25a2 Compare May 2, 2024 18:09
@justinmc justinmc self-requested a review May 2, 2024 22:01
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 👍 but maybe @chunhtai should also give a final sign off. Sorry for the slow review!


class _SelectableRegionContainerDelegate extends MultiSelectableSelectionContainerDelegate {
/// A delegate that manages updating multiple [Selectable] children where the
/// [Selectable]s do not change or move around frequently.
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 point people towards MultiSelectableSelectionContainerDelegate for cases that DO move around? If that's not already obvious.

Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at this nowadays, not sure if I understood correctly but there is a reference to MultiSelectableSelectionContainerDelegate in the "See also" so seems good now.

return super.dispatchSelectionEventToChild(selectable, event);
}

/// Ensures the [Selectable] child has received up to date selection event.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: "event" => "events"? Or "the most up to date selection event"?

Comment on lines +4280 to +5355
/// Copies the selected contents of all [Selectable]s, separating their
/// contents with a new line.
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh was this PR prompted by the issue about putting a newline between copied widgets?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, and anyone trying to use SelectionContainer has to re-implement or copy and paste a lot of this logic since it requires a SelectionContainerDelegate.

Comment on lines 505 to 675
child: SelectionContainer(
delegate: selectionDelegate,
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this be done near the top of the widget tree for a big app with a single SelectionContainer, so that nearly everything has this newline behavior? Or would you need to do a bunch of SelectionContainers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would still need multiple SelectionContainers because getSelectedContent does not propagate down the tree.

@chunhtai chunhtai self-requested a review May 13, 2024 16:40
@chunhtai
Copy link
Contributor

Hi @Renzo-Olivares, are you still planning on coming back to this pr?

@Renzo-Olivares
Copy link
Contributor Author

@chunhtai I plan to come back to this soon.

@Renzo-Olivares Renzo-Olivares force-pushed the public-selectioncontainerdelegate branch from 6df88be to 0ff440c Compare August 1, 2024 20:03
@Renzo-Olivares Renzo-Olivares force-pushed the public-selectioncontainerdelegate branch from 49e7b41 to 61c1da3 Compare August 15, 2024 19:26
/// This method is called when:
/// 1. A new [Selectable] is added to the delegate, and its screen location
/// falls into the previous selection.
/// 2. A [Selectable] is dispatched a [SelectionEvent] of type
Copy link
Contributor

Choose a reason for hiding this comment

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

before a [SelectionEvent] is dispatched to a [Selectable]

/// When `forEnd` is null, the [Selectable] will be registered as having received both
/// start and end events.
@protected
void trackInternalSelectionStateForSelectable({required Selectable selectable, bool? forEnd}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The naming for these method should be describing what condition will trigger this method instead of describing what the internal logic does. Something like
didReceiveSelectionEdgeEventFor();.

The current name may not make sense after several level of subclassing

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

@Renzo-Olivares Renzo-Olivares Aug 19, 2024

Choose a reason for hiding this comment

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

I think handle* may be confusing since we already have methods like handleSelectWord, and handleSelectionEdgeUpdate. didReceive makes a good distinction since these methods are only tracking the events and not actually doing any of the logic that moves a selection edge.

/// When `forEnd` is true, the [Selectable] will be registered as having received
/// an end event. When false, the [Selectable] is registered as having received
/// a start event.
///
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 also mention in what condition subclass may want to override/calling this method?

/// and [currentSelectionEndIndex] should be set to valid values at the time
/// this method is called.
@protected
void updateInternalSelectionStateForBoundaryEvents() {
Copy link
Contributor

Choose a reason for hiding this comment

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

same, this should probably named didReceiveSelectionBoundaryEvents

Copy link
Contributor

Choose a reason for hiding this comment

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

same for other methods

/// This indicates that the given `selectable` has neither received a
/// start or end [SelectionEdgeUpdateEvent]s.
@protected
void clearInternalSelectionStateForSelectable(Selectable selectable) {
Copy link
Contributor

Choose a reason for hiding this comment

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

is there any subclass that uses this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not at the moment, but it is used by public methods like remove and dispatchSelectionEventToChild, so I think if someone extends this delegate they may want access to this method.

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 but I'm still deferring to @chunhtai for final approval.

Does this PR also solve the problem of #146625?

Also just a general question about SelectionContainer, probably unrelated to this PR. Can I just stick the SelectionContainer directly under the SelectableRegion if I want to affect everything that it makes selectable? Just thinking that that's probably how most people will use this.

final LayerLink _endHandleLayerLink = LayerLink();
final LayerLink _toolbarLayerLink = LayerLink();
final _SelectableRegionContainerDelegate _selectionDelegate = _SelectableRegionContainerDelegate();
final MultiStaticSelectableSelectionContainerDelegate _selectionDelegate = MultiStaticSelectableSelectionContainerDelegate();
Copy link
Contributor

Choose a reason for hiding this comment

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

Just wanted to check if this name would be any better if it was shorter. Maybe MultiStaticSelectableDelegate or StaticSelectablesSelectionContainerDelegate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to StaticSelectionContainerDelegate, open to other suggestions. We have a private _ScrollableSelectionContainerDelegate, so I think this name makes sense given that convention.


class _SelectableRegionContainerDelegate extends MultiSelectableSelectionContainerDelegate {
/// A delegate that manages updating multiple [Selectable] children where the
/// [Selectable]s do not change or move around frequently.
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at this nowadays, not sure if I understood correctly but there is a reference to MultiSelectableSelectionContainerDelegate in the "See also" so seems good now.

/// When `forEnd` is null, the [Selectable] will be registered as having received both
/// start and end events.
@protected
void trackInternalSelectionStateForSelectable({required Selectable selectable, bool? forEnd}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment on lines 673 to 674
child: Center(
child: SelectionContainer(
Copy link
Contributor

Choose a reason for hiding this comment

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

Super nit: I wonder if it's better to put the SelectionContainer above the Center? I'm guessing most people will use this to cover everything under as single SelectableRegion. Or is there any benefit to keeping this as-is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes sense to me, updated.

@flutter-dashboard
Copy link

This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@Piinks
Copy link
Contributor

Piinks commented Sep 24, 2024

(PR triage): What is the status if this PR? :)

@Renzo-Olivares Renzo-Olivares force-pushed the public-selectioncontainerdelegate branch 2 times, most recently from 8c3c351 to 4c71b37 Compare September 25, 2024 18:43
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.

Renewing my LGTM 👍

Comment on lines 1874 to 1885
if (forEnd == null) {
_hasReceivedStartEvent.add(selectable);
_hasReceivedEndEvent.add(selectable);
return;
}
if (forEnd) {
_hasReceivedEndEvent.add(selectable);
} else {
_hasReceivedStartEvent.add(selectable);
}
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 a switch on forEnd?

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.

LGTM, except for the comments

/// locations for start and end [SelectionEdgeUpdateEvent]s.
@protected
void clearInternalSelectionState() {
_hasReceivedStartEvent.clear();
Copy link
Contributor

Choose a reason for hiding this comment

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

If we expose clearInternalSelectionStateForSelectable, this should be a for loop to call clearInternalSelectionStateForSelectable

/// This indicates that the given `selectable` has neither received a
/// start or end [SelectionEdgeUpdateEvent]s.
@protected
void clearInternalSelectionStateForSelectable(Selectable selectable) {
Copy link
Contributor

Choose a reason for hiding this comment

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

the doc should mention sub class should clean up state they added in didReceive- method

Copy link
Contributor

Choose a reason for hiding this comment

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

same for the didReceive method that state should be clean up in this method

@Renzo-Olivares Renzo-Olivares force-pushed the public-selectioncontainerdelegate branch from d2ac9fb to 1dae4ff Compare October 30, 2024 19:36
@justinmc justinmc added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 7, 2024
@auto-submit auto-submit bot merged commit 97eef6a into flutter:master Nov 7, 2024
75 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 8, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 8, 2024
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Nov 11, 2024
flutter/flutter@73546b3...c8510f2

2024-11-08 [email protected] Roll Flutter Engine from 44d788f4651b to a08bd5a07c2a (3 revisions) (flutter/flutter#158375)
2024-11-08 [email protected] Add ability to override `NavigationDestination.label` padding for `NavigationBar` (flutter/flutter#158260)
2024-11-08 [email protected] Add flutter/package code generation instructions (flutter/flutter#158326)
2024-11-08 [email protected] Roll Flutter Engine from 8e19915c19fc to 44d788f4651b (3 revisions) (flutter/flutter#158362)
2024-11-08 [email protected] Roll Flutter Engine from bcb281cde579 to 8e19915c19fc (4 revisions) (flutter/flutter#158354)
2024-11-07 [email protected] Make `_SelectableRegionSelectionContainerDelegate` public (flutter/flutter#147080)
2024-11-07 [email protected] Manual roll Flutter Engine from 371c86fb6b49 to bcb281cde579 (flutter/flutter#158346)
2024-11-07 [email protected] Add clarification on review timelines in PR template (flutter/flutter#158345)
2024-11-07 [email protected] Increase Java heap limit to 8GB for plugin integration tests using deferred components (flutter/flutter#158330)
2024-11-07 [email protected] Roll pub packages (flutter/flutter#158337)
2024-11-07 [email protected] Roll Flutter Engine from ac50b20ae5c9 to 371c86fb6b49 (5 revisions) (flutter/flutter#158336)
2024-11-07 [email protected] Fix a breakage caused by the test being unskipped. (flutter/flutter#158335)
2024-11-07 [email protected] Roll Flutter Engine from 8a963cfc134c to ac50b20ae5c9 (1 revision) (flutter/flutter#158308)
2024-11-07 [email protected] `Plugin.isDevDependency` if exclusively in `dev_dependencies` (flutter/flutter#157462)
2024-11-07 [email protected] Add recently imported packages to issue template (flutter/flutter#158324)
2024-11-07 [email protected] Roll Flutter Engine from 076688d95818 to 8a963cfc134c (1 revision) (flutter/flutter#158304)
2024-11-07 [email protected] Roll Flutter Engine from 94dac953a95f to 076688d95818 (2 revisions) (flutter/flutter#158303)
2024-11-07 [email protected] Make leak tracking bots blocking. (flutter/flutter#157866)
2024-11-07 [email protected] Roll Flutter Engine from b36ca3319825 to 94dac953a95f (1 revision) (flutter/flutter#158297)
2024-11-06 [email protected] Roll Flutter Engine from 58ac1dadd69d to b36ca3319825 (9 revisions) (flutter/flutter#158295)
2024-11-06 [email protected] Added cusor control properties to CupertinoSearchTextField and tests (flutter/flutter#158240)
2024-11-06 [email protected] Fix flakiness in hot_reload_test.dart (flutter/flutter#158271)
2024-11-06 [email protected] Fix use of deprecated `buildDir` in Android templates/tests/examples (flutter/flutter#157560)
2024-11-06 [email protected] Roll Flutter Engine from f03f11300a9d to 58ac1dadd69d (5 revisions) (flutter/flutter#158283)
2024-11-06 [email protected] Roll pub packages (flutter/flutter#158281)
2024-11-06 [email protected] Delete firebase_android_embedding_v2_smoke_test (flutter/flutter#158223)
2024-11-06 [email protected] [web] fix --ab option for web benchmarks (flutter/flutter#154574)
2024-11-06 [email protected] excluding website-cms from critical pr triage (flutter/flutter#158220)
2024-11-06 [email protected] Add test for `image.frame_builder.0.dart` (flutter/flutter#158247)
2024-11-06 [email protected] Roll Packages from 7219431 to bb5a258 (6 revisions) (flutter/flutter#158267)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 12, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 6, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 7, 2025
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 autosubmit Merge PR when tree becomes green via auto submit App framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants