blob: 6a79ef1a6146efa71b83339271629ad5d78fba88 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
@interface FlutterMutatorView (Private)
@property(readonly, nonatomic, nonnull) NSMutableArray<NSView*>* pathClipViews;
@property(readonly, nonatomic, nullable) NSView* platformViewContainer;
@end
namespace {
void ApplyFlutterLayer(FlutterMutatorView* view,
FlutterSize size,
const std::vector<FlutterPlatformViewMutation>& mutations) {
FlutterLayer layer;
layer.struct_size = sizeof(FlutterLayer);
layer.type = kFlutterLayerContentTypePlatformView;
// Offset is ignored by mutator view, the bounding rect is determined by
// width and transform.
layer.offset = FlutterPoint{0, 0};
layer.size = size;
FlutterPlatformView flutterPlatformView;
flutterPlatformView.struct_size = sizeof(FlutterPlatformView);
flutterPlatformView.identifier = 0;
std::vector<const FlutterPlatformViewMutation*> mutationPointers;
for (auto& mutation : mutations) {
mutationPointers.push_back(&mutation);
}
flutterPlatformView.mutations = mutationPointers.data();
flutterPlatformView.mutations_count = mutationPointers.size();
layer.platform_view = &flutterPlatformView;
[view applyFlutterLayer:&layer];
}
} // namespace
TEST(FlutterMutatorViewTest, BasicFrameIsCorrect) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
EXPECT_EQ(mutatorView.platformView, platformView);
std::vector<FlutterPlatformViewMutation> mutations{
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
};
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
EXPECT_EQ(mutatorView.pathClipViews.count, 0ull);
EXPECT_NE(mutatorView.platformViewContainer, nil);
}
TEST(FlutterMutatorViewTest, TransformedFrameIsCorrect) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
NSView* mutatorViewParent = [[NSView alloc] init];
mutatorViewParent.wantsLayer = YES;
mutatorViewParent.layer.contentsScale = 2.0;
[mutatorViewParent addSubview:mutatorView];
std::vector<FlutterPlatformViewMutation> mutations{
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 2,
.scaleY = 2,
},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1.5,
.transX = -7.5,
.scaleY = 1.5,
.transY = -5,
},
},
};
// PlatformView size form engine comes in physical pixels
ApplyFlutterLayer(mutatorView, FlutterSize{30 * 2, 20 * 2}, mutations);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(92.5, 45, 45, 30)));
EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
EXPECT_TRUE(
CATransform3DEqualToTransform(mutatorView.platformViewContainer.layer.sublayerTransform,
CATransform3DMakeScale(1.5, 1.5, 1)));
}
TEST(FlutterMutatorViewTest, FrameWithLooseClipIsCorrect) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
EXPECT_EQ(mutatorView.platformView, platformView);
std::vector<FlutterPlatformViewMutation> mutations{
{
.type = kFlutterPlatformViewMutationTypeClipRect,
.clip_rect = FlutterRect{80, 40, 200, 100},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
};
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
}
TEST(FlutterMutatorViewTest, FrameWithTightClipIsCorrect) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
EXPECT_EQ(mutatorView.platformView, platformView);
std::vector<FlutterPlatformViewMutation> mutations{
{
.type = kFlutterPlatformViewMutationTypeClipRect,
.clip_rect = FlutterRect{80, 40, 200, 100},
},
{
.type = kFlutterPlatformViewMutationTypeClipRect,
.clip_rect = FlutterRect{110, 55, 120, 65},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
};
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 55, 10, 10)));
EXPECT_TRUE(
CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -5, 30, 20)));
EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
}
TEST(FlutterMutatorViewTest, FrameWithTightClipAndTransformIsCorrect) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
NSView* mutatorViewParent = [[NSView alloc] init];
mutatorViewParent.wantsLayer = YES;
mutatorViewParent.layer.contentsScale = 2.0;
[mutatorViewParent addSubview:mutatorView];
std::vector<FlutterPlatformViewMutation> mutations{
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 2,
.scaleY = 2,
},
},
{
.type = kFlutterPlatformViewMutationTypeClipRect,
.clip_rect = FlutterRect{80, 40, 200, 100},
},
{
.type = kFlutterPlatformViewMutationTypeClipRect,
.clip_rect = FlutterRect{110, 55, 120, 65},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1.5,
.transX = -7.5,
.scaleY = 1.5,
.transY = -5,
},
},
};
ApplyFlutterLayer(mutatorView, FlutterSize{30 * 2, 20 * 2}, mutations);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 55, 10, 10)));
EXPECT_TRUE(
CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-17.5, -10, 45, 30)));
EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
}
// Rounded rectangle without hitting the corner
TEST(FlutterMutatorViewTest, RoundRectClipsToSimpleRectangle) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
std::vector<FlutterPlatformViewMutation> mutations{
{
.type = kFlutterPlatformViewMutationTypeClipRoundedRect,
.clip_rounded_rect =
FlutterRoundedRect{
.rect = FlutterRect{110, 30, 120, 90},
.upper_left_corner_radius = FlutterSize{10, 10},
.upper_right_corner_radius = FlutterSize{10, 10},
.lower_right_corner_radius = FlutterSize{10, 10},
.lower_left_corner_radius = FlutterSize{10, 10},
},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
};
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 50, 10, 20)));
EXPECT_TRUE(
CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, 0, 30, 20)));
EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
EXPECT_EQ(mutatorView.pathClipViews.count, 0ul);
}
TEST(FlutterMutatorViewTest, RoundRectClipsToPath) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
std::vector<FlutterPlatformViewMutation> mutations{
{
.type = kFlutterPlatformViewMutationTypeClipRoundedRect,
.clip_rounded_rect =
FlutterRoundedRect{
.rect = FlutterRect{110, 60, 150, 150},
.upper_left_corner_radius = FlutterSize{10, 10},
.upper_right_corner_radius = FlutterSize{10, 10},
.lower_right_corner_radius = FlutterSize{10, 10},
.lower_left_corner_radius = FlutterSize{10, 10},
},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
};
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 60, 20, 10)));
EXPECT_TRUE(
CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -10, 30, 20)));
EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
EXPECT_TRUE(
CATransform3DEqualToTransform(mutatorView.pathClipViews.firstObject.layer.mask.transform,
CATransform3DMakeTranslation(-100, -50, 0)));
}
TEST(FlutterMutatorViewTest, PathClipViewsAreAddedAndRemoved) {
NSView* platformView = [[NSView alloc] init];
FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
std::vector<FlutterPlatformViewMutation> mutations{
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
};
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
EXPECT_EQ(mutatorView.pathClipViews.count, 0ull);
std::vector<FlutterPlatformViewMutation> mutations2{
{
.type = kFlutterPlatformViewMutationTypeClipRoundedRect,
.clip_rounded_rect =
FlutterRoundedRect{
.rect = FlutterRect{110, 60, 150, 150},
.upper_left_corner_radius = FlutterSize{10, 10},
.upper_right_corner_radius = FlutterSize{10, 10},
.lower_right_corner_radius = FlutterSize{10, 10},
.lower_left_corner_radius = FlutterSize{10, 10},
},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
};
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations2);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 60, 20, 10)));
EXPECT_TRUE(
CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -10, 30, 20)));
EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
EXPECT_EQ(platformView.superview, mutatorView.platformViewContainer);
EXPECT_EQ(mutatorView.platformViewContainer.superview, mutatorView.pathClipViews[0]);
EXPECT_EQ(mutatorView.pathClipViews[0].superview, mutatorView);
std::vector<FlutterPlatformViewMutation> mutations3{
{
.type = kFlutterPlatformViewMutationTypeClipRoundedRect,
.clip_rounded_rect =
FlutterRoundedRect{
.rect = FlutterRect{110, 55, 150, 150},
.upper_left_corner_radius = FlutterSize{10, 10},
.upper_right_corner_radius = FlutterSize{10, 10},
.lower_right_corner_radius = FlutterSize{10, 10},
.lower_left_corner_radius = FlutterSize{10, 10},
},
},
{
.type = kFlutterPlatformViewMutationTypeClipRoundedRect,
.clip_rounded_rect =
FlutterRoundedRect{
.rect = FlutterRect{30, 30, 120, 65},
.upper_left_corner_radius = FlutterSize{10, 10},
.upper_right_corner_radius = FlutterSize{10, 10},
.lower_right_corner_radius = FlutterSize{10, 10},
.lower_left_corner_radius = FlutterSize{10, 10},
},
},
{
.type = kFlutterPlatformViewMutationTypeTransformation,
.transformation =
FlutterTransformation{
.scaleX = 1,
.transX = 100,
.scaleY = 1,
.transY = 50,
},
},
};
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations3);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 55, 10, 10)));
EXPECT_TRUE(
CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -5, 30, 20)));
EXPECT_EQ(mutatorView.pathClipViews.count, 2ul);
EXPECT_EQ(platformView.superview, mutatorView.platformViewContainer);
EXPECT_EQ(mutatorView.platformViewContainer.superview, mutatorView.pathClipViews[1]);
EXPECT_EQ(mutatorView.pathClipViews[1].superview, mutatorView.pathClipViews[0]);
EXPECT_EQ(mutatorView.pathClipViews[0].superview, mutatorView);
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations2);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 60, 20, 10)));
EXPECT_TRUE(
CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -10, 30, 20)));
EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
EXPECT_EQ(platformView.superview, mutatorView.platformViewContainer);
EXPECT_EQ(mutatorView.platformViewContainer.superview, mutatorView.pathClipViews[0]);
EXPECT_EQ(mutatorView.pathClipViews[0].superview, mutatorView);
ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
EXPECT_EQ(mutatorView.pathClipViews.count, 0ull);
}