Skip to content
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

migrate rendering to nullsafety #64456

Closed
wants to merge 26 commits into from
Closed

Conversation

a14n
Copy link
Contributor

@a14n a14n commented Aug 24, 2020

Description

NNBD migration for rendering

Related Issues

Tests

I added the following tests:

None

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.
  • The analyzer (flutter analyze --flutter-repo) does not report any problems on my PR.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them? Please read Handling breaking changes.

@flutter-dashboard flutter-dashboard bot added the framework flutter/packages/flutter repository. See also f: labels. label Aug 24, 2020
@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 Hixie on the #hackers channel in Chat.

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

@skia-gold
Copy link

Gold has detected about 3 untriaged digest(s) on patchset 1.
View them at https://flutter-gold.skia.org/cl/github/64456

@skia-gold
Copy link

Gold has detected about 3 untriaged digest(s) on patchset 1.
View them at https://flutter-gold.skia.org/cl/github/64456

@a14n a14n force-pushed the nnbd-rendering branch 2 times, most recently from ed0efae to f14df20 Compare August 24, 2020 15:16
@@ -634,7 +633,7 @@ class BoxConstraints extends Constraints {
///
/// * [RenderBox.hitTest], which documents more details around hit testing
/// [RenderBox]es.
typedef BoxHitTest = bool Function(BoxHitTestResult result, Offset position);
typedef BoxHitTest = bool Function(BoxHitTestResult result, Offset? position);
Copy link
Contributor

Choose a reason for hiding this comment

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

position should be non-nullable

Copy link
Contributor

Choose a reason for hiding this comment

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

here i also added:

/// Method signature for hit testing a [RenderBox] with a manually
/// managed position (one that is passed out-of-band).
///
/// Used by [RenderSliverSingleBoxAdapter.hitTestBoxChild] to hit test
/// [RenderBox] children of a [RenderSliver].
///
/// See also:
///
///  * [RenderBox.hitTest], which documents more details around hit testing
///    [RenderBox]es.
typedef BoxHitTestWithOutOfBandPosition = bool Function(BoxHitTestResult result);

@required Offset position,
@required BoxHitTest hitTest,
required Matrix4? transform,
required Offset? position,
Copy link
Contributor

Choose a reason for hiding this comment

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

position should be non nullable

@required BoxHitTest hitTest,
required Matrix4? transform,
required Offset? position,
required BoxHitTest hitTest,
}) {
assert(hitTest != null);
if (transform != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

assert position isn't null

@required Offset position,
@required BoxHitTest hitTest,
required Offset? offset,
required Offset? position,
Copy link
Contributor

Choose a reason for hiding this comment

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

here i think position should be non-nullable

Comment on lines +770 to 772
final Offset? transformedPosition = position == null || offset == null
? position
: position - offset;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
final Offset? transformedPosition = position == null || offset == null
? position
: position - offset;
final Offset transformedPosition = offset == null ? position : position - offset;

@required Offset position,
@required BoxHitTest hitTest,
required Matrix4? transform,
required Offset? position,
Copy link
Contributor

Choose a reason for hiding this comment

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

non-nullable position here too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But the doc explicitally mentions null:

  /// The `position` argument may be null, which will be forwarded to the
  /// `hitTest` callback as-is. Using null as the position can be useful if
  /// the child speaks a different hit test protocol then the parent and the
  /// position is not required to do the actual hit testing in that protocol.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I'm proposing removing that and replacing it with dedicated methods to handle that case.

Comment on lines +813 to 815
final Offset? transformedPosition = position == null || transform == null
? position
: MatrixUtils.transformPoint(transform, position);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
final Offset? transformedPosition = position == null || transform == null
? position
: MatrixUtils.transformPoint(transform, position);
assert(position != null);
final Offset transformedPosition = transform == null ?
position : MatrixUtils.transformPoint(transform, position);

}) {
assert(hitTest != null);
final Offset transformedPosition = position == null || transform == null
final Offset? transformedPosition = position == null || transform == null
? position
: MatrixUtils.transformPoint(transform, position);
if (transform != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

at the end of this class i added:

  /// Pass-through method for adding a hit test while manually managing
  /// the position transformation logic.
  ///
  /// The actual hit testing of the child needs to be implemented in the
  /// provided `hitTest` callback. The position needs to be handled by
  /// the caller.
  ///
  /// The function returns the return value of the `hitTest` callback.
  ///
  /// A `paintOffset`, `paintTransform`, or `rawTransform` should be
  /// passed to the method to update the hit test stack.
  ///
  ///  * `paintOffset` has the semantics of the `offset` passed to
  ///    [addWithPaintOffset].
  ///
  ///  * `paintTransform` has the semantics of the `transform` passed to
  ///    [addWithPaintTransform], except thit it must be invertible; it
  ///    is the responsibility of the caller to ensure this.
  ///
  ///  * `rawTransform` has the semantics of the `transform` passed to
  ///    [addWithRawTransform].
  ///
  /// Exactly one of these must be non-null.
  ///
  /// See also:
  ///
  ///  * [addWithPaintTransform], which takes a generic paint transform matrix and
  ///    documents the intended usage of this API in more detail.
  bool addWithOutOfBandPosition({
    Offset? paintOffset,
    Matrix4? paintTransform,
    Matrix4? rawTransform,
    required BoxHitTestWithOutOfBandPosition hitTest,
  }) {
    assert(hitTest != null);
    assert((paintOffset == null && paintTransform == null && rawTransform != null) ||
           (paintOffset == null && paintTransform != null && rawTransform == null) ||
           (paintOffset != null && paintTransform == null && rawTransform == null));
    if (paintOffset != null) {
      pushOffset(-paintOffset);
    } else if (rawTransform != null) {
      pushTransform(rawTransform);
    } else {
      assert(paintTransform != null);
      paintTransform = Matrix4.tryInvert(PointerEvent.removePerspectiveTransform(paintTransform!));
      assert(paintTransform != null);
      pushTransform(paintTransform!);
    }
    final bool isHit = hitTest(this);
    popTransform();
    return isHit;
  }

@@ -311,7 +310,7 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
/// When the style is [CacheExtentStyle.viewport], it is the main axis extent
/// of the viewport multiplied by the requested cache extent, which is still
/// expressed in pixels.
double _calculatedCacheExtent;
double? _calculatedCacheExtent;

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// TODO(ianh): cacheExtent/cacheExtentStyle should be a single
// object that specifies both the scalar value and the unit, not a
// pair of independent setters. Changing that would allow a more
// rational API and would let us make the getter non-nullable.

@@ -311,7 +310,7 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
/// When the style is [CacheExtentStyle.viewport], it is the main axis extent
/// of the viewport multiplied by the requested cache extent, which is still
/// expressed in pixels.
double _calculatedCacheExtent;
double? _calculatedCacheExtent;

/// {@template flutter.rendering.viewport.cacheExtentStyle}
/// Controls how the [cacheExtent] is interpreted.
Copy link
Contributor

@Hixie Hixie Aug 24, 2020

Choose a reason for hiding this comment

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

replace from next line until just before the getter:

  /// If set to [CacheExtentStyle.pixel], the [cacheExtent] will be
  /// treated as a logical pixels, and the default [cacheExtent] is
  /// [RenderAbstractViewport.defaultCacheExtent].
  ///
  /// If set to [CacheExtentStyle.viewport], the [cacheExtent] will be
  /// treated as a multiplier for the main axis extent of the
  /// viewport. In this case there is no default [cacheExtent]; it
  /// must be explicitly specified.
  /// {@endtemplate}
  ///
  /// Changing the [cacheExtentStyle] without also changing the [cacheExtent]
  /// is rarely the correct choice.

@@ -297,7 +296,7 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
/// {@endtemplate}
double get cacheExtent => _cacheExtent;
Copy link
Contributor

Choose a reason for hiding this comment

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

replace from the getter until the assert:

  ///
  /// The getter can never return null, but the field is nullable
  /// because the setter can be set to null to reset the value to
  /// [RenderAbstractViewport.defaultCacheExtent] (in which case
  /// [cacheExtentStyle] must be [CacheExtentStyle.pixel]).
  ///
  /// See also:
  ///
  ///  * [cacheExtentStyle], which controls the units of the [cacheExtent].
  double? get cacheExtent => _cacheExtent;
  double _cacheExtent;
  set cacheExtent(double? value) {
    value ??= RenderAbstractViewport.defaultCacheExtent;

Comment on lines 672 to +676
applyPaintTransform(child, transform);
final bool isHit = result.addWithPaintTransform(
transform: transform,
position: null, // Manually adapting from box to sliver position below.
hitTest: (BoxHitTestResult result, Offset _) {
hitTest: (BoxHitTestResult result, Offset? _) {
Copy link
Contributor

@Hixie Hixie Aug 24, 2020

Choose a reason for hiding this comment

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

Suggested change
applyPaintTransform(child, transform);
final bool isHit = result.addWithPaintTransform(
transform: transform,
position: null, // Manually adapting from box to sliver position below.
hitTest: (BoxHitTestResult result, Offset _) {
hitTest: (BoxHitTestResult result, Offset? _) {
applyPaintTransform(child, transform); // must be invertible
final bool isHit = result.addWithOutOfBandPosition(
paintTransform: transform,
hitTest: (BoxHitTestResult result) {

@@ -1510,10 +1507,10 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
void updateOutOfBandData(GrowthDirection growthDirection, SliverGeometry childLayoutGeometry) {
switch (growthDirection) {
case GrowthDirection.forward:
_maxScrollExtent += childLayoutGeometry.scrollExtent;
_maxScrollExtent = _maxScrollExtent + childLayoutGeometry.scrollExtent;
Copy link
Contributor

Choose a reason for hiding this comment

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

+= should work fine here since _maxScrollExtent is late non-nullable

break;
case GrowthDirection.reverse:
_minScrollExtent -= childLayoutGeometry.scrollExtent;
_minScrollExtent = _minScrollExtent - childLayoutGeometry.scrollExtent;
Copy link
Contributor

Choose a reason for hiding this comment

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

similar here

current = childBefore(current);
}
return pinnedExtent;
}
return null;
}

@override
Copy link
Contributor

Choose a reason for hiding this comment

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

please add this inside the applyPaintTransform method:

    // Hit test logic relies on this always providing an invertible matrix.

while (current != child) {
pinnedExtent += current.geometry.maxScrollObstructionExtent;
pinnedExtent += current!.geometry!.maxScrollObstructionExtent;
current = childAfter(current);
}
return pinnedExtent;
Copy link
Contributor

Choose a reason for hiding this comment

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

add to applyPaintTransform:

    // Hit test logic relies on this always providing an invertible matrix.

@@ -95,7 +93,7 @@ abstract class ViewportOffset extends ChangeNotifier {
///
/// This object notifies its listeners when this value changes (except when
/// the value changes due to [correctBy]).
double get pixels;
double? get pixels;
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 this should be non-nullable. it would simplify a ton of other code.

@Hixie
Copy link
Contributor

Hixie commented Aug 24, 2020

In addition to my comments above, the following changes would also be good to do at the same time (some are required by some of the changes I suggested):

https://github.com/flutter/flutter/pull/64476/files#diff-acd2b473dd8ae3b5cab347ba47c55c94R902 until the end of the PR

@Hixie
Copy link
Contributor

Hixie commented Aug 26, 2020

I applied the rest of my review comments and uploaded #64621, please feel free to continue from there if there's more work to be done.

@a14n
Copy link
Contributor Author

a14n commented Aug 27, 2020

Continued in #64621

@a14n a14n closed this Aug 27, 2020
@a14n a14n deleted the nnbd-rendering branch August 28, 2020 07:21
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 28, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants