Skip to content

Commit 0975e96

Browse files
ryancatfacebook-github-bot
authored andcommitted
Fix transform when calculate overflowInset
Summary: This diff fixes overflowInset calculation when a shadow node has transform matrix from a transfrom prop in JS. Specifically, this fixed the use case when transform directly used on a view component. When using Animated.View, it will create an invisible wrapper which will behave correctly with existing logic. This diff bring both use cases to work properly. When a shadow node has transform on it, it will affect the overflowInset values for its parent nodes, but won't affect its own or any of its child nodes overflowInset values. This is obvious for translateX/Y case, but not for scale case. Take a look at the following case: ``` ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │Original Layout │ │ Translate AB │ │ Scale AB │ └────────────────┘ └────────────────┘ └────────────────┘ ─────▶ ◀───── ─────▶ ┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ┐ ┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ─ ┐ │ A │ │ A │ │ A │ │ │ │ │ │ │ │ │ ├ ─ ─ ─ ─ ─ ┼ ─ ─┌─────┤─ ─ ─ ─ ─ ┤ ─ ─ ─ ─│─ ─ ─┌───┐┼ ─ ─ ─ ─ │ │ ◀─ ─ ─ │ │AB │ ─ ─ ─▶ │ │ │AB ││ │ │ ┌ ─ ─ ┼ ─ ─ ─ ┬──┴┬ ─ ─ ─ ─ ┤ │ │ │ │ │ └─────┤ ├┘ └───────┤AB │ └────┤ │ │ │┌──┴─────────┤ │ │ │ │ │ │ │ │ ┌───┴──────────┤ ││ABC │ │┌──┴─────────┐ │ │ABC │ │ │└──┬─────────┤ │ │ │ ││ABC │ │ │ │ │ │ │ ┌───ABD───────┴─┐ │ │ │└──┬─────────┘ │ ▼ │ └───┬──────────┘ ├─────────────┬─┘ │ │ │ │ ├───ABD───────┴─┐ │ │ │ ├────────────────┴──┐ │ │ ─ ─ ─ ─ ─ ─ ─└───┘─ ─ ─ ─ ─ ▼ └─────────────┬─┘ │ ▼ │ ABD │ │ └ ┴ ─ ─ ─ ─ ─ ─ ┴───┴ ─ ─ ─ ─ ┘ ├────────────────┬──┘ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ┴─────┴ ─ ─ ─ ─ ─ ``` For the translate case, only view A has change on the overflowInset values for `right` and `bottom`. Note that the `left` and `top` are not changed as we union before and after transform is applied. For the scale case, similar things are happening for view A, and both `left`, `right`, and `bottom` values are increased. However, for View AB or any of its children, they only *appear* to be increased, but that is purely cosmetic as it's caused by transform. The actual values are not changed, which will later be converted during render phase to actual pixels on screen. In summary, overflowInset is affected from child nodes transform matrix to the current node (bottom up), but not from transform matrix on the current node to child nodes (top down). So the correct way to apply transform is to make it only affect calculating `contentFrame` during layout, which collects child nodes layout information and their transforms. The `contentFrame` is then used to decide the overflowInset values for the parent node. The current transform matrix on parent node is never used as it's not affecting overflowInset for the current node or its child nodes. This diff reflects the context above with added unit test to cover the scale and translate transform matrix. Changelog: [Android/IOS][Fixed] - Fixed how we calculate overflowInset with transform matrix Reviewed By: sammy-SC Differential Revision: D34433404 fbshipit-source-id: 0e48e4af4cfd5a6dd32a30e7667686e8ef1a7004
1 parent 3eae11e commit 0975e96

File tree

4 files changed

+232
-30
lines changed

4 files changed

+232
-30
lines changed

ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp

+22-11
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,6 @@ void YogaLayoutableShadowNode::layout(LayoutContext layoutContext) {
479479
react_native_assert(!yogaNode_.isDirty());
480480

481481
auto contentFrame = Rect{};
482-
483482
for (auto childYogaNode : yogaNode_.getChildren()) {
484483
auto &childNode =
485484
*static_cast<YogaLayoutableShadowNode *>(childYogaNode->getContext());
@@ -521,24 +520,36 @@ void YogaLayoutableShadowNode::layout(LayoutContext layoutContext) {
521520

522521
auto layoutMetricsWithOverflowInset = childNode.getLayoutMetrics();
523522
if (layoutMetricsWithOverflowInset.displayType != DisplayType::None) {
523+
// The contentFrame should always union with existing child node layout +
524+
// overflowInset. The transform may in a deferred animation and not
525+
// applied yet.
524526
contentFrame.unionInPlace(insetBy(
525527
layoutMetricsWithOverflowInset.frame,
526528
layoutMetricsWithOverflowInset.overflowInset));
529+
530+
auto childTransform = childNode.getTransform();
531+
if (childTransform != Transform::Identity()) {
532+
// The child node's transform matrix will affect the parent node's
533+
// contentFrame. We need to union with child node's after transform
534+
// layout here.
535+
contentFrame.unionInPlace(insetBy(
536+
layoutMetricsWithOverflowInset.frame * childTransform,
537+
layoutMetricsWithOverflowInset.overflowInset * childTransform));
538+
}
527539
}
528540
}
529541

530542
if (yogaNode_.getStyle().overflow() == YGOverflowVisible) {
531-
auto transform = getTransform();
532-
auto transformedContentFrame = contentFrame;
533-
if (Transform::Identity() != transform) {
534-
// When animation uses native driver, Yoga has no knowledge of the
535-
// animation. In case the content goes out from current container, we need
536-
// to union the content frame with its transformed frame.
537-
transformedContentFrame = contentFrame * getTransform();
538-
transformedContentFrame.unionInPlace(contentFrame);
539-
}
543+
// Note that the parent node's overflow layout is NOT affected by its
544+
// transform matrix. That transform matrix is applied on the parent node as
545+
// well as all of its child nodes, which won't cause changes on the
546+
// overflowInset values. A special note on the scale transform -- the scaled
547+
// layout may look like it's causing overflowInset changes, but it's purely
548+
// cosmetic and will be handled by pixel density conversion logic later when
549+
// render the view. The actual overflowInset value is not changed as if the
550+
// transform is not happening here.
540551
layoutMetrics_.overflowInset =
541-
calculateOverflowInset(layoutMetrics_.frame, transformedContentFrame);
552+
calculateOverflowInset(layoutMetrics_.frame, contentFrame);
542553
} else {
543554
layoutMetrics_.overflowInset = {};
544555
}

ReactCommon/react/renderer/components/view/tests/LayoutTest.cpp

+196-19
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
namespace facebook {
1818
namespace react {
1919

20+
// Note: the (x, y) origin is always relative to the parent node. You may use
21+
// P482342650 to re-create this test case in playground.
22+
2023
// *******************************************************┌─ABCD:────┐****
2124
// *******************************************************│ {70,-50} │****
2225
// *******************************************************│ {30,60} │****
@@ -43,6 +46,8 @@ namespace react {
4346
// ***********************│ │************************************
4447
// ***********************└──────────┘************************************
4548

49+
enum TestCase { AS_IS, CLIPPING, TRANSFORM_SCALE, TRANSFORM_TRANSLATE };
50+
4651
class LayoutTest : public ::testing::Test {
4752
protected:
4853
ComponentBuilder builder_;
@@ -55,7 +60,7 @@ class LayoutTest : public ::testing::Test {
5560

5661
LayoutTest() : builder_(simpleComponentBuilder()) {}
5762

58-
void initialize(bool enforceClippingForABC) {
63+
void initialize(TestCase testCase) {
5964
// clang-format off
6065
auto element =
6166
Element<RootShadowNode>()
@@ -73,6 +78,7 @@ class LayoutTest : public ::testing::Test {
7378
.children({
7479
Element<ViewShadowNode>()
7580
.reference(viewShadowNodeA_)
81+
.tag(2)
7682
.props([] {
7783
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
7884
auto &props = *sharedProps;
@@ -85,7 +91,8 @@ class LayoutTest : public ::testing::Test {
8591
.children({
8692
Element<ViewShadowNode>()
8793
.reference(viewShadowNodeAB_)
88-
.props([] {
94+
.tag(3)
95+
.props([=] {
8996
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
9097
auto &props = *sharedProps;
9198
auto &yogaStyle = props.yogaStyle;
@@ -94,17 +101,26 @@ class LayoutTest : public ::testing::Test {
94101
yogaStyle.position()[YGEdgeTop] = YGValue{10, YGUnitPoint};
95102
yogaStyle.dimensions()[YGDimensionWidth] = YGValue{30, YGUnitPoint};
96103
yogaStyle.dimensions()[YGDimensionHeight] = YGValue{90, YGUnitPoint};
104+
105+
if (testCase == TRANSFORM_SCALE) {
106+
props.transform = props.transform * Transform::Scale(2, 2, 1);
107+
}
108+
109+
if (testCase == TRANSFORM_TRANSLATE) {
110+
props.transform = props.transform * Transform::Translate(10, 10, 0);
111+
}
97112
return sharedProps;
98113
})
99114
.children({
100115
Element<ViewShadowNode>()
101116
.reference(viewShadowNodeABC_)
117+
.tag(4)
102118
.props([=] {
103119
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
104120
auto &props = *sharedProps;
105121
auto &yogaStyle = props.yogaStyle;
106122

107-
if (enforceClippingForABC) {
123+
if (testCase == CLIPPING) {
108124
yogaStyle.overflow() = YGOverflowHidden;
109125
}
110126

@@ -118,6 +134,7 @@ class LayoutTest : public ::testing::Test {
118134
.children({
119135
Element<ViewShadowNode>()
120136
.reference(viewShadowNodeABCD_)
137+
.tag(5)
121138
.props([] {
122139
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
123140
auto &props = *sharedProps;
@@ -132,6 +149,7 @@ class LayoutTest : public ::testing::Test {
132149
}),
133150
Element<ViewShadowNode>()
134151
.reference(viewShadowNodeABE_)
152+
.tag(6)
135153
.props([] {
136154
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
137155
auto &props = *sharedProps;
@@ -154,32 +172,191 @@ class LayoutTest : public ::testing::Test {
154172
}
155173
};
156174

175+
// Test the layout as described above with no extra changes
157176
TEST_F(LayoutTest, overflowInsetTest) {
158-
initialize(false);
177+
initialize(AS_IS);
178+
179+
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
159180

160-
auto layoutMetrics = viewShadowNodeA_->getLayoutMetrics();
181+
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
182+
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
161183

162-
EXPECT_EQ(layoutMetrics.frame.size.width, 50);
163-
EXPECT_EQ(layoutMetrics.frame.size.height, 50);
184+
EXPECT_EQ(layoutMetricsA.overflowInset.left, -50);
185+
EXPECT_EQ(layoutMetricsA.overflowInset.top, -30);
186+
EXPECT_EQ(layoutMetricsA.overflowInset.right, -80);
187+
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -50);
164188

165-
EXPECT_EQ(layoutMetrics.overflowInset.left, -50);
166-
EXPECT_EQ(layoutMetrics.overflowInset.top, -30);
167-
EXPECT_EQ(layoutMetrics.overflowInset.right, -80);
168-
EXPECT_EQ(layoutMetrics.overflowInset.bottom, -50);
189+
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
190+
191+
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
192+
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
193+
194+
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
195+
EXPECT_EQ(layoutMetricsABC.overflowInset.top, -50);
196+
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
197+
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
169198
}
170199

200+
// Test when box ABC has clipping (aka overflow hidden)
171201
TEST_F(LayoutTest, overflowInsetWithClippingTest) {
172-
initialize(true);
202+
initialize(CLIPPING);
203+
204+
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
205+
206+
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
207+
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
208+
209+
EXPECT_EQ(layoutMetricsA.overflowInset.left, -50);
210+
EXPECT_EQ(layoutMetricsA.overflowInset.top, 0);
211+
EXPECT_EQ(layoutMetricsA.overflowInset.right, -80);
212+
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -50);
213+
214+
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
215+
216+
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
217+
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
218+
219+
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
220+
EXPECT_EQ(layoutMetricsABC.overflowInset.top, 0);
221+
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
222+
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
223+
}
224+
225+
// Test when box AB translate (10, 10, 0) in transform. The parent node's
226+
// overflowInset will be affected, but the transformed node and its child nodes
227+
// are not affected. Here is an example:
228+
//
229+
// ┌────────────────┐ ┌────────────────┐
230+
// │Original Layout │ │ Translate AB │
231+
// └────────────────┘ └────────────────┘
232+
// ─────▶
233+
// ┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ┐ ┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ─ ┐
234+
// │ A │ │ A │
235+
// │ │ │ │ │ │ │ │
236+
// ─ ─ ─ ─│─ ─ ─┌───┐┼ ─ ─ ─ ─ │ │
237+
// │ │ │AB ││ │ │ ┌ ─ ─ ┼ ─ ─ ─ ┬──┴┬ ─ ─ ─ ─ ┤
238+
// └─────┤ ├┘ └───────┤AB │
239+
// │ │┌──┴─────────┤ │ │ │ │ │
240+
// ││ABC │ │┌──┴─────────┐
241+
// │ │└──┬─────────┤ │ │ │ ││ABC │
242+
// ┌───ABD───────┴─┐ │ │ │└──┬─────────┘
243+
// ├─────────────┬─┘ │ │ │ │ ├───ABD───────┴─┐ │ │
244+
// ─ ─ ─ ─ ─ ─ ─└───┘─ ─ ─ ─ ─ ▼ └─────────────┬─┘ │
245+
// └ ┴ ─ ─ ─ ─ ─ ─ ┴───┴ ─ ─ ─ ─ ┘
246+
247+
TEST_F(LayoutTest, overflowInsetTransformTranslateTest) {
248+
initialize(TRANSFORM_TRANSLATE);
249+
250+
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
251+
252+
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
253+
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
254+
255+
// Change on parent node
256+
// The top/left values are NOT changing as overflowInset is union of before
257+
// and after transform layout. In this case, we move to the right and bottom,
258+
// so the left and top is not changing, while right and bottom values are
259+
// increased.
260+
EXPECT_EQ(layoutMetricsA.overflowInset.left, -50);
261+
EXPECT_EQ(layoutMetricsA.overflowInset.top, -30);
262+
EXPECT_EQ(layoutMetricsA.overflowInset.right, -90);
263+
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -60);
264+
265+
auto layoutMetricsAB = viewShadowNodeAB_->getLayoutMetrics();
266+
267+
EXPECT_EQ(layoutMetricsAB.frame.size.width, 30);
268+
EXPECT_EQ(layoutMetricsAB.frame.size.height, 90);
269+
270+
// No change on self node with translate transform
271+
EXPECT_EQ(layoutMetricsAB.overflowInset.left, -60);
272+
EXPECT_EQ(layoutMetricsAB.overflowInset.top, -40);
273+
EXPECT_EQ(layoutMetricsAB.overflowInset.right, -90);
274+
EXPECT_EQ(layoutMetricsAB.overflowInset.bottom, 0);
275+
276+
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
277+
278+
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
279+
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
280+
281+
// No change on child node
282+
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
283+
EXPECT_EQ(layoutMetricsABC.overflowInset.top, -50);
284+
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
285+
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
286+
}
287+
288+
// Test when box AB scaled 2X in transform. The parent node's overflowInset will
289+
// be affected. However, the transformed node and its child nodes only appears
290+
// to be affected (dashed arrow). Since all transform is cosmetic only, the
291+
// actual values are NOT changed. It will be converted later when mapping the
292+
// values to pixels during rendering. Here is an example:
293+
//
294+
// ┌────────────────┐ ┌────────────────┐
295+
// │Original Layout │ │ Scale AB │
296+
// └────────────────┘ └────────────────┘
297+
// ─────▶
298+
// ┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ─ ┐
299+
// │ A │ │ A │
300+
// │ │ │ │ ├ ─ ─ ─ ─ ─ ┼ ─ ─┌─────┤─ ─ ─ ─ ─ ┤
301+
// ─ ─ ─ ─│─ ─ ─┌───┐┼ ─ ─ ─ ─ │ │AB │ ─ ─ ─▶
302+
// │ │ │AB ││ │ │ │ │ │ │
303+
// └─────┤ ├┘ └────┤ │
304+
// │ │┌──┴─────────┤ │ │ ┌───┴──────────┤
305+
// ││ABC │ │ │ABC │
306+
// │ │└──┬─────────┤ │ │ │ │ │
307+
// ┌───ABD───────┴─┐ │ │ │ └───┬──────────┘
308+
// ├─────────────┬─┘ │ │ │ ├────────────────┴──┐ │ │
309+
// ─ ─ ─ ─ ─ ─ ─└───┘─ ─ ─ ─ ─ ▼ │ ABD │ │
310+
// ├────────────────┬──┘ │ │
311+
// ─ ─ ─ ─ ─ ─ ─ ─ ┴─────┴ ─ ─ ─ ─ ─
312+
313+
TEST_F(LayoutTest, overflowInsetTransformScaleTest) {
314+
initialize(TRANSFORM_SCALE);
315+
316+
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
317+
318+
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
319+
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
320+
321+
// Change on parent node when a child view scale up
322+
// Note that AB scale up from its center point. The numbers are calculated
323+
// assuming AB's center point is not moving.
324+
EXPECT_EQ(layoutMetricsA.overflowInset.left, -125);
325+
EXPECT_EQ(layoutMetricsA.overflowInset.top, -115);
326+
EXPECT_EQ(layoutMetricsA.overflowInset.right, -185);
327+
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -95);
328+
329+
auto layoutMetricsAB = viewShadowNodeAB_->getLayoutMetrics();
330+
331+
// The frame of box AB won't actually scale up. The transform matrix is
332+
// purely cosmetic and should apply later in mounting phase.
333+
EXPECT_EQ(layoutMetricsAB.frame.size.width, 30);
334+
EXPECT_EQ(layoutMetricsAB.frame.size.height, 90);
335+
336+
// No change on self node with scale transform. This may sound a bit
337+
// surprising, but the overflowInset values will be scaled up via pixel
338+
// density ratio along with width/height of the view. When we do hit-testing,
339+
// the overflowInset value will appears to be doubled as expected.
340+
EXPECT_EQ(layoutMetricsAB.overflowInset.left, -60);
341+
EXPECT_EQ(layoutMetricsAB.overflowInset.top, -40);
342+
EXPECT_EQ(layoutMetricsAB.overflowInset.right, -90);
343+
EXPECT_EQ(layoutMetricsAB.overflowInset.bottom, 0);
173344

174-
auto layoutMetrics = viewShadowNodeA_->getLayoutMetrics();
345+
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
175346

176-
EXPECT_EQ(layoutMetrics.frame.size.width, 50);
177-
EXPECT_EQ(layoutMetrics.frame.size.height, 50);
347+
// The frame of box ABC won't actually scale up. The transform matrix is
348+
// purely cosmatic and should apply later in mounting phase.
349+
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
350+
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
178351

179-
EXPECT_EQ(layoutMetrics.overflowInset.left, -50);
180-
EXPECT_EQ(layoutMetrics.overflowInset.top, 0);
181-
EXPECT_EQ(layoutMetrics.overflowInset.right, -80);
182-
EXPECT_EQ(layoutMetrics.overflowInset.bottom, -50);
352+
// The overflowInset of ABC won't change either. This may sound a bit
353+
// surprising, but the overflowInset values will be scaled up via pixel
354+
// density ratio along with width/height of the view. When we do hit-testing,
355+
// the overflowInset value will appears to be doubled as expected.
356+
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
357+
EXPECT_EQ(layoutMetricsABC.overflowInset.top, -50);
358+
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
359+
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
183360
}
184361

185362
} // namespace react

ReactCommon/react/renderer/graphics/Transform.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,14 @@ Rect operator*(Rect const &rect, Transform const &transform) {
360360
transformedA, transformedB, transformedC, transformedD);
361361
}
362362

363+
EdgeInsets operator*(EdgeInsets const &edgeInsets, Transform const &transform) {
364+
return EdgeInsets{
365+
edgeInsets.left * transform.matrix[0],
366+
edgeInsets.top * transform.matrix[5],
367+
edgeInsets.right * transform.matrix[0],
368+
edgeInsets.bottom * transform.matrix[5]};
369+
}
370+
363371
Vector operator*(Transform const &transform, Vector const &vector) {
364372
return {
365373
vector.x * transform.at(0, 0) + vector.y * transform.at(1, 0) +

ReactCommon/react/renderer/graphics/Transform.h

+6
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@ Size operator*(Size const &size, Transform const &transform);
180180
*/
181181
Rect operator*(Rect const &rect, Transform const &transform);
182182

183+
/*
184+
* Applies tranformation to the given EdgeInsets.
185+
* ONLY SUPPORTS scale transformation.
186+
*/
187+
EdgeInsets operator*(EdgeInsets const &edgeInsets, Transform const &transform);
188+
183189
Vector operator*(Transform const &transform, Vector const &vector);
184190

185191
} // namespace react

0 commit comments

Comments
 (0)