react-native-windows 0.74.22 → 0.74.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Microsoft.ReactNative/CompositionComponentView.idl +2 -1
- package/Microsoft.ReactNative/Fabric/AbiComponentDescriptor.cpp +4 -1
- package/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.cpp +1 -1
- package/Microsoft.ReactNative/Fabric/AbiViewProps.cpp +7 -0
- package/Microsoft.ReactNative/Fabric/AbiViewProps.h +2 -0
- package/Microsoft.ReactNative/Fabric/ComponentView.cpp +11 -4
- package/Microsoft.ReactNative/Fabric/ComponentView.h +3 -2
- package/Microsoft.ReactNative/Fabric/Composition/BorderPrimitive.cpp +926 -0
- package/Microsoft.ReactNative/Fabric/Composition/BorderPrimitive.h +76 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +31 -13
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +27 -3
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +3 -1
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +193 -892
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +17 -22
- package/Microsoft.ReactNative/Fabric/Composition/DebuggingOverlayComponentView.cpp +1 -1
- package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +129 -122
- package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.h +14 -8
- package/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp +34 -20
- package/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h +5 -3
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +40 -2
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h +9 -0
- package/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp +31 -3
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +8 -8
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +31 -1
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +4 -0
- package/Microsoft.ReactNative/Fabric/Composition/Theme.cpp +9 -3
- package/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp +11 -0
- package/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h +2 -0
- package/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl +1 -0
- package/Microsoft.ReactNative/IReactViewComponentBuilder.idl +1 -1
- package/Microsoft.ReactNative/ReactNativeAppBuilder.cpp +25 -129
- package/Microsoft.ReactNative/ReactNativeAppBuilder.h +5 -13
- package/Microsoft.ReactNative/ReactNativeAppBuilder.idl +13 -34
- package/Microsoft.ReactNative/ReactNativeIsland.idl +3 -2
- package/Microsoft.ReactNative/ReactNativeWin32App.cpp +129 -18
- package/Microsoft.ReactNative/ReactNativeWin32App.h +14 -5
- package/Microsoft.ReactNative/ViewProps.idl +2 -0
- package/PropertySheets/Generated/PackageVersion.g.props +3 -3
- package/Shared/Shared.vcxitems +3 -10
- package/Shared/Shared.vcxitems.filters +1 -0
- package/package.json +3 -3
- package/templates/cpp-app/template.config.js +1 -1
- package/templates/cpp-app/windows/MyApp/MyApp.cpp +46 -130
- package/templates/cpp-lib/template.config.js +1 -1
- package/Microsoft.ReactNative/ReactInstanceSettingsBuilder.cpp +0 -59
- package/Microsoft.ReactNative/ReactInstanceSettingsBuilder.h +0 -23
|
@@ -32,6 +32,23 @@
|
|
|
32
32
|
|
|
33
33
|
namespace winrt::Microsoft::ReactNative::Composition::implementation {
|
|
34
34
|
|
|
35
|
+
constexpr float FOCUS_VISUAL_WIDTH = 2.0f;
|
|
36
|
+
|
|
37
|
+
// m_outerVisual
|
|
38
|
+
// |
|
|
39
|
+
// |
|
|
40
|
+
// ----- m_visual <-- Background / clip - Can be a custom visual depending on Component type
|
|
41
|
+
// |
|
|
42
|
+
// ----- Border Visuals x N (BorderPrimitive attached to m_visual)
|
|
43
|
+
// ------Focus Visual Container
|
|
44
|
+
// |
|
|
45
|
+
// |------Inner Focus Visual
|
|
46
|
+
// |
|
|
47
|
+
// ------ Border Visuals x N (BorderPrimitive)
|
|
48
|
+
// |------Outer Focus Visual
|
|
49
|
+
// |
|
|
50
|
+
// ------ Border Visuals x N (BorderPrimitive)
|
|
51
|
+
|
|
35
52
|
ComponentView::ComponentView(
|
|
36
53
|
const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
|
|
37
54
|
facebook::react::Tag tag,
|
|
@@ -40,8 +57,9 @@ ComponentView::ComponentView(
|
|
|
40
57
|
: base_type(tag, reactContext), m_compContext(compContext), m_flags(flags) {
|
|
41
58
|
m_outerVisual = compContext.CreateSpriteVisual(); // TODO could be a raw ContainerVisual if we had a
|
|
42
59
|
// CreateContainerVisual in ICompositionContext
|
|
43
|
-
m_focusVisual = compContext.
|
|
44
|
-
|
|
60
|
+
m_focusVisual = compContext.CreateSpriteVisual(); // TODO could be a raw ContainerVisual if we had a
|
|
61
|
+
// CreateContainerVisual in ICompositionContext
|
|
62
|
+
m_outerVisual.InsertAt(m_focusVisual, 0);
|
|
45
63
|
}
|
|
46
64
|
|
|
47
65
|
ComponentView::~ComponentView() {
|
|
@@ -68,9 +86,17 @@ void ComponentView::onThemeChanged() noexcept {
|
|
|
68
86
|
}
|
|
69
87
|
}
|
|
70
88
|
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
if (m_borderPrimitive) {
|
|
90
|
+
m_borderPrimitive->onThemeChanged(
|
|
91
|
+
m_layoutMetrics, BorderPrimitive::resolveAndAlignBorderMetrics(m_layoutMetrics, *viewProps()));
|
|
92
|
+
}
|
|
93
|
+
if (m_focusInnerPrimitive) {
|
|
94
|
+
auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
|
|
95
|
+
m_focusInnerPrimitive->onThemeChanged(innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
|
|
96
|
+
}
|
|
97
|
+
if (m_focusOuterPrimitive) {
|
|
98
|
+
auto outerFocusMetrics = focusLayoutMetrics(true /*inner*/);
|
|
99
|
+
m_focusOuterPrimitive->onThemeChanged(outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
|
|
74
100
|
}
|
|
75
101
|
|
|
76
102
|
if ((m_flags & ComponentViewFeatures::ShadowProps) == ComponentViewFeatures::ShadowProps) {
|
|
@@ -131,13 +157,21 @@ void ComponentView::updateProps(
|
|
|
131
157
|
}
|
|
132
158
|
}
|
|
133
159
|
|
|
134
|
-
if (
|
|
135
|
-
|
|
160
|
+
if (m_borderPrimitive) {
|
|
161
|
+
m_borderPrimitive->updateProps(oldViewProps, newViewProps);
|
|
162
|
+
}
|
|
163
|
+
if (!newViewProps.enableFocusRing) {
|
|
164
|
+
showFocusVisual(false);
|
|
165
|
+
}
|
|
166
|
+
if (m_focusInnerPrimitive) {
|
|
167
|
+
m_focusInnerPrimitive->updateProps(oldViewProps, newViewProps);
|
|
168
|
+
}
|
|
169
|
+
if (m_focusOuterPrimitive) {
|
|
170
|
+
m_focusOuterPrimitive->updateProps(oldViewProps, newViewProps);
|
|
136
171
|
}
|
|
137
172
|
if ((m_flags & ComponentViewFeatures::ShadowProps) == ComponentViewFeatures::ShadowProps) {
|
|
138
173
|
updateShadowProps(oldViewProps, newViewProps);
|
|
139
174
|
}
|
|
140
|
-
|
|
141
175
|
if (oldViewProps.tooltip != newViewProps.tooltip) {
|
|
142
176
|
if (!m_tooltipTracked && newViewProps.tooltip) {
|
|
143
177
|
TooltipService::GetCurrent(m_reactContext.Properties())->StartTracking(*this);
|
|
@@ -155,13 +189,58 @@ void ComponentView::updateLayoutMetrics(
|
|
|
155
189
|
facebook::react::LayoutMetrics const &layoutMetrics,
|
|
156
190
|
facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept {
|
|
157
191
|
if ((m_flags & ComponentViewFeatures::NativeBorder) == ComponentViewFeatures::NativeBorder) {
|
|
158
|
-
|
|
192
|
+
updateClippingPath(layoutMetrics, *viewProps());
|
|
193
|
+
OuterVisual().Size(
|
|
194
|
+
{layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
|
|
195
|
+
layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
|
|
196
|
+
OuterVisual().Offset({
|
|
197
|
+
layoutMetrics.frame.origin.x * layoutMetrics.pointScaleFactor,
|
|
198
|
+
layoutMetrics.frame.origin.y * layoutMetrics.pointScaleFactor,
|
|
199
|
+
0.0f,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
updateFocusLayoutMetrics(layoutMetrics);
|
|
204
|
+
|
|
205
|
+
if (layoutMetrics != oldLayoutMetrics) {
|
|
206
|
+
if (m_borderPrimitive) {
|
|
207
|
+
m_borderPrimitive->markNeedsUpdate();
|
|
208
|
+
}
|
|
209
|
+
if (m_focusInnerPrimitive) {
|
|
210
|
+
m_focusInnerPrimitive->markNeedsUpdate();
|
|
211
|
+
}
|
|
212
|
+
if (m_focusOuterPrimitive) {
|
|
213
|
+
m_focusOuterPrimitive->markNeedsUpdate();
|
|
214
|
+
}
|
|
159
215
|
}
|
|
160
216
|
|
|
161
217
|
base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
|
|
162
218
|
UpdateCenterPropertySet();
|
|
163
219
|
}
|
|
164
220
|
|
|
221
|
+
void ComponentView::updateFocusLayoutMetrics(facebook::react::LayoutMetrics const &layoutMetrics) noexcept {
|
|
222
|
+
if (m_focusInnerPrimitive) {
|
|
223
|
+
auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
|
|
224
|
+
m_focusInnerPrimitive->RootVisual().Size(
|
|
225
|
+
{innerFocusMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
|
|
226
|
+
innerFocusMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
|
|
227
|
+
m_focusInnerPrimitive->RootVisual().Offset(
|
|
228
|
+
{-FOCUS_VISUAL_WIDTH * layoutMetrics.pointScaleFactor,
|
|
229
|
+
-FOCUS_VISUAL_WIDTH * layoutMetrics.pointScaleFactor,
|
|
230
|
+
0.0f});
|
|
231
|
+
}
|
|
232
|
+
if (m_focusOuterPrimitive) {
|
|
233
|
+
auto outerFocusMetrics = focusLayoutMetrics(false /*inner*/);
|
|
234
|
+
m_focusOuterPrimitive->RootVisual().Size(
|
|
235
|
+
{outerFocusMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
|
|
236
|
+
outerFocusMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
|
|
237
|
+
m_focusOuterPrimitive->RootVisual().Offset(
|
|
238
|
+
{-(FOCUS_VISUAL_WIDTH * 2 * m_layoutMetrics.pointScaleFactor),
|
|
239
|
+
-(FOCUS_VISUAL_WIDTH * 2 * m_layoutMetrics.pointScaleFactor),
|
|
240
|
+
0.0f});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
165
244
|
const facebook::react::LayoutMetrics &ComponentView::layoutMetrics() const noexcept {
|
|
166
245
|
return m_layoutMetrics;
|
|
167
246
|
}
|
|
@@ -199,7 +278,23 @@ void ComponentView::FinalizeTransform(
|
|
|
199
278
|
|
|
200
279
|
void ComponentView::FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) noexcept {
|
|
201
280
|
if ((m_flags & ComponentViewFeatures::NativeBorder) == ComponentViewFeatures::NativeBorder) {
|
|
202
|
-
|
|
281
|
+
auto borderMetrics = BorderPrimitive::resolveAndAlignBorderMetrics(m_layoutMetrics, *viewProps());
|
|
282
|
+
if (!m_borderPrimitive && BorderPrimitive::requiresBorder(borderMetrics, theme())) {
|
|
283
|
+
m_borderPrimitive = std::make_shared<BorderPrimitive>(*this, Visual());
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (m_borderPrimitive) {
|
|
287
|
+
m_borderPrimitive->finalize(m_layoutMetrics, borderMetrics);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (m_focusInnerPrimitive) {
|
|
292
|
+
auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
|
|
293
|
+
m_focusInnerPrimitive->finalize(innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
|
|
294
|
+
}
|
|
295
|
+
if (m_focusOuterPrimitive) {
|
|
296
|
+
auto outerFocusMetrics = focusLayoutMetrics(false /*inner*/);
|
|
297
|
+
m_focusOuterPrimitive->finalize(outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
|
|
203
298
|
}
|
|
204
299
|
|
|
205
300
|
if (m_FinalizeTransform) {
|
|
@@ -226,7 +321,7 @@ void ComponentView::onGotFocus(
|
|
|
226
321
|
const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
|
|
227
322
|
if (args.OriginalSource() == Tag()) {
|
|
228
323
|
m_eventEmitter->onFocus();
|
|
229
|
-
if (
|
|
324
|
+
if (viewProps()->enableFocusRing) {
|
|
230
325
|
showFocusVisual(true);
|
|
231
326
|
}
|
|
232
327
|
if (m_uiaProvider) {
|
|
@@ -325,6 +420,10 @@ void ComponentView::ReleasePointerCapture(
|
|
|
325
420
|
->ReleasePointerCapture(pointer, static_cast<facebook::react::Tag>(Tag()));
|
|
326
421
|
}
|
|
327
422
|
|
|
423
|
+
void ComponentView::SetViewFeatures(ComponentViewFeatures viewFeatures) noexcept {
|
|
424
|
+
m_flags = viewFeatures;
|
|
425
|
+
}
|
|
426
|
+
|
|
328
427
|
RECT ComponentView::getClientRect() const noexcept {
|
|
329
428
|
RECT rc{0};
|
|
330
429
|
facebook::react::Point parentOffset{0};
|
|
@@ -356,877 +455,81 @@ const facebook::react::SharedViewEventEmitter &ComponentView::GetEventEmitter()
|
|
|
356
455
|
return m_eventEmitter;
|
|
357
456
|
}
|
|
358
457
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
ComponentView::SpecialBorderLayerCount>
|
|
362
|
-
ComponentView::FindSpecialBorderLayers() const noexcept {
|
|
363
|
-
std::array<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual, SpecialBorderLayerCount> layers{
|
|
364
|
-
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
|
|
365
|
-
|
|
366
|
-
if (m_numBorderVisuals) {
|
|
367
|
-
for (uint8_t i = 0; i < m_numBorderVisuals; i++) {
|
|
368
|
-
auto visual = Visual().GetAt(i);
|
|
369
|
-
layers[i] = visual.as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>();
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
return layers;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
struct RoundedPathParameters {
|
|
377
|
-
float topLeftRadiusX = 0;
|
|
378
|
-
float topLeftRadiusY = 0;
|
|
379
|
-
float topRightRadiusX = 0;
|
|
380
|
-
float topRightRadiusY = 0;
|
|
381
|
-
float bottomRightRadiusX = 0;
|
|
382
|
-
float bottomRightRadiusY = 0;
|
|
383
|
-
float bottomLeftRadiusX = 0;
|
|
384
|
-
float bottomLeftRadiusY = 0;
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
/*
|
|
388
|
-
* Creates and returns a PathGeometry object used to clip the visuals of an element when a BorderRadius is set.
|
|
389
|
-
* Can also be used as part of a GeometryGroup for drawing a rounded border / innerstroke when called from
|
|
390
|
-
* GetGeometryForRoundedBorder. "params" defines the radii (horizontal and vertical) for each corner (top left, top
|
|
391
|
-
* right, bottom right, bottom left). "rectPathGeometry" defines the bounding box of the generated shape.
|
|
392
|
-
*/
|
|
393
|
-
static winrt::com_ptr<ID2D1PathGeometry> GenerateRoundedRectPathGeometry(
|
|
394
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
|
|
395
|
-
const RoundedPathParameters ¶ms,
|
|
396
|
-
const facebook::react::RectangleEdges<float> &rectPathGeometry) noexcept {
|
|
397
|
-
winrt::com_ptr<ID2D1PathGeometry> pathGeometry;
|
|
398
|
-
winrt::com_ptr<ID2D1Factory1> spD2dFactory;
|
|
399
|
-
compContext.as<::Microsoft::ReactNative::Composition::Experimental::ICompositionContextInterop>()->D2DFactory(
|
|
400
|
-
spD2dFactory.put());
|
|
401
|
-
|
|
402
|
-
// Create a path geometry.
|
|
403
|
-
HRESULT hr = spD2dFactory->CreatePathGeometry(pathGeometry.put());
|
|
404
|
-
if (FAILED(hr)) {
|
|
405
|
-
assert(false);
|
|
406
|
-
return nullptr;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Write to the path geometry using the geometry sink.
|
|
410
|
-
winrt::com_ptr<ID2D1GeometrySink> spSink = nullptr;
|
|
411
|
-
hr = pathGeometry->Open(spSink.put());
|
|
412
|
-
|
|
413
|
-
if (FAILED(hr)) {
|
|
414
|
-
assert(false);
|
|
415
|
-
return nullptr;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
float left = rectPathGeometry.left;
|
|
419
|
-
float right = rectPathGeometry.right;
|
|
420
|
-
float top = rectPathGeometry.top;
|
|
421
|
-
float bottom = rectPathGeometry.bottom;
|
|
422
|
-
|
|
423
|
-
// This function uses Cubic Beziers to approximate Arc segments, even though D2D supports arcs.
|
|
424
|
-
// This is INTENTIONAL. D2D Arc Segments are eventually converted into cubic beziers, but this
|
|
425
|
-
// is done in such a way that we don't have control over how many bezier curve segments are used
|
|
426
|
-
// for each arc. We need to ensure that we always use the same number of control points so that
|
|
427
|
-
// our paths can be used in a PathKeyFrameAnimation.
|
|
428
|
-
// Value for control point scale factor derived from methods described in:
|
|
429
|
-
// https://web.archive.org/web/20200322075504/http://itc.ktu.lt/index.php/ITC/article/download/11812/6479
|
|
430
|
-
constexpr float controlPointScaleFactor = 0.44771528244f; // 1 - (4 * (sqrtf(2.0f) - 1) / 3.0f);
|
|
431
|
-
|
|
432
|
-
#ifdef DEBUG
|
|
433
|
-
// std::sqrtf is not constexpr, so we precalculated this and wrote it in a constexpr form above.
|
|
434
|
-
// On debug, we should still check that the values are equivalent, though.
|
|
435
|
-
static float calculatedScaleFactor = 1 - (4 * (sqrtf(2.0f) - 1) / 3.0f);
|
|
436
|
-
assert(controlPointScaleFactor == calculatedScaleFactor);
|
|
437
|
-
#endif // DEBUG
|
|
438
|
-
|
|
439
|
-
bool needsConsistentNumberOfControlPoints = true; // VisualVersion::IsUseWinCompClippingRegionEnabled();
|
|
440
|
-
|
|
441
|
-
if (needsConsistentNumberOfControlPoints || (params.topLeftRadiusX != 0.0 && params.topLeftRadiusY != 0.0)) {
|
|
442
|
-
spSink->BeginFigure(D2D1::Point2F(left + params.topLeftRadiusX, top), D2D1_FIGURE_BEGIN_FILLED);
|
|
443
|
-
} else {
|
|
444
|
-
spSink->BeginFigure(D2D1::Point2F(left, top), D2D1_FIGURE_BEGIN_FILLED);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Move to the top right corner
|
|
448
|
-
spSink->AddLine(D2D1::Point2F(right - params.topRightRadiusX, top));
|
|
449
|
-
if (needsConsistentNumberOfControlPoints) {
|
|
450
|
-
D2D1_BEZIER_SEGMENT arcSegmentTopRight = {
|
|
451
|
-
D2D1::Point2F(right - controlPointScaleFactor * params.topRightRadiusX, top),
|
|
452
|
-
D2D1::Point2F(right, top + controlPointScaleFactor * params.topRightRadiusY),
|
|
453
|
-
D2D1::Point2F(right, top + params.topRightRadiusY)};
|
|
454
|
-
|
|
455
|
-
spSink->AddBezier(&arcSegmentTopRight);
|
|
456
|
-
} else if (params.topRightRadiusX != 0.0 && params.topRightRadiusY != 0.0) {
|
|
457
|
-
D2D1_ARC_SEGMENT arcSegmentTopRight = {
|
|
458
|
-
D2D1::Point2F(right, top + params.topRightRadiusY),
|
|
459
|
-
D2D1::SizeF(params.topRightRadiusX, params.topRightRadiusY),
|
|
460
|
-
0.0f,
|
|
461
|
-
D2D1_SWEEP_DIRECTION_CLOCKWISE,
|
|
462
|
-
D2D1_ARC_SIZE_SMALL};
|
|
463
|
-
|
|
464
|
-
spSink->AddArc(&arcSegmentTopRight);
|
|
465
|
-
} else {
|
|
466
|
-
spSink->AddLine(D2D1::Point2F(right, top));
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// Move to the bottom right corner
|
|
470
|
-
spSink->AddLine(D2D1::Point2F(right, bottom - params.bottomRightRadiusY));
|
|
471
|
-
if (needsConsistentNumberOfControlPoints) {
|
|
472
|
-
D2D1_BEZIER_SEGMENT arcSegmentBottomRight = {
|
|
473
|
-
D2D1::Point2F(right, bottom - controlPointScaleFactor * params.bottomRightRadiusY),
|
|
474
|
-
D2D1::Point2F(right - controlPointScaleFactor * params.bottomRightRadiusX, bottom),
|
|
475
|
-
D2D1::Point2F(right - params.bottomRightRadiusX, bottom)};
|
|
476
|
-
|
|
477
|
-
spSink->AddBezier(&arcSegmentBottomRight);
|
|
478
|
-
} else if (params.bottomRightRadiusX != 0.0 && params.bottomRightRadiusY != 0.0) {
|
|
479
|
-
D2D1_ARC_SEGMENT arcSegmentBottomRight = {
|
|
480
|
-
D2D1::Point2F(right - params.bottomRightRadiusX, bottom),
|
|
481
|
-
D2D1::SizeF(params.bottomRightRadiusX, params.bottomRightRadiusY),
|
|
482
|
-
0.0f,
|
|
483
|
-
D2D1_SWEEP_DIRECTION_CLOCKWISE,
|
|
484
|
-
D2D1_ARC_SIZE_SMALL};
|
|
485
|
-
|
|
486
|
-
spSink->AddArc(&arcSegmentBottomRight);
|
|
487
|
-
} else {
|
|
488
|
-
spSink->AddLine(D2D1::Point2F(right, bottom));
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// Move to the bottom left corner
|
|
492
|
-
spSink->AddLine(D2D1::Point2F(left + params.bottomLeftRadiusX, bottom));
|
|
493
|
-
if (needsConsistentNumberOfControlPoints) {
|
|
494
|
-
D2D1_BEZIER_SEGMENT arcSegmentBottomLeft = {
|
|
495
|
-
D2D1::Point2F(left + controlPointScaleFactor * params.bottomLeftRadiusX, bottom),
|
|
496
|
-
D2D1::Point2F(left, bottom - controlPointScaleFactor * params.bottomLeftRadiusY),
|
|
497
|
-
D2D1::Point2F(left, bottom - params.bottomLeftRadiusY)};
|
|
498
|
-
|
|
499
|
-
spSink->AddBezier(&arcSegmentBottomLeft);
|
|
500
|
-
} else if (params.bottomLeftRadiusX != 0.0 && params.bottomLeftRadiusY != 0.0) {
|
|
501
|
-
D2D1_ARC_SEGMENT arcSegmentBottomLeft = {
|
|
502
|
-
D2D1::Point2F(left, bottom - params.bottomLeftRadiusY),
|
|
503
|
-
D2D1::SizeF(params.bottomLeftRadiusX, params.bottomLeftRadiusY),
|
|
504
|
-
0.0f,
|
|
505
|
-
D2D1_SWEEP_DIRECTION_CLOCKWISE,
|
|
506
|
-
D2D1_ARC_SIZE_SMALL};
|
|
507
|
-
|
|
508
|
-
spSink->AddArc(&arcSegmentBottomLeft);
|
|
509
|
-
} else {
|
|
510
|
-
spSink->AddLine(D2D1::Point2F(left, bottom));
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// Move to the top left corner
|
|
514
|
-
spSink->AddLine(D2D1::Point2F(left, top + params.topLeftRadiusY));
|
|
515
|
-
if (needsConsistentNumberOfControlPoints) {
|
|
516
|
-
D2D1_BEZIER_SEGMENT arcSegmentTopLeft = {
|
|
517
|
-
D2D1::Point2F(left, top + controlPointScaleFactor * params.topLeftRadiusY),
|
|
518
|
-
D2D1::Point2F(left + controlPointScaleFactor * params.topLeftRadiusX, top),
|
|
519
|
-
D2D1::Point2F(left + params.topLeftRadiusX, top)};
|
|
520
|
-
|
|
521
|
-
spSink->AddBezier(&arcSegmentTopLeft);
|
|
522
|
-
} else if (params.topLeftRadiusX != 0.0 && params.topLeftRadiusY != 0.0) {
|
|
523
|
-
D2D1_ARC_SEGMENT arcSegmentTopLeft = {
|
|
524
|
-
D2D1::Point2F(left + params.topLeftRadiusX, top),
|
|
525
|
-
D2D1::SizeF(params.topLeftRadiusX, params.topLeftRadiusY),
|
|
526
|
-
0.0f,
|
|
527
|
-
D2D1_SWEEP_DIRECTION_CLOCKWISE,
|
|
528
|
-
D2D1_ARC_SIZE_SMALL};
|
|
529
|
-
|
|
530
|
-
spSink->AddArc(&arcSegmentTopLeft);
|
|
531
|
-
} else {
|
|
532
|
-
spSink->AddLine(D2D1::Point2F(left, top));
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
spSink->EndFigure(D2D1_FIGURE_END_CLOSED);
|
|
536
|
-
spSink->Close();
|
|
537
|
-
|
|
538
|
-
return pathGeometry;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
RoundedPathParameters GenerateRoundedPathParameters(
|
|
542
|
-
const facebook::react::RectangleCorners<float> &baseRadius,
|
|
543
|
-
const facebook::react::RectangleEdges<float> &inset,
|
|
544
|
-
const facebook::react::Size &pathSize) noexcept {
|
|
545
|
-
RoundedPathParameters result;
|
|
546
|
-
|
|
547
|
-
if (pathSize.width == 0 || pathSize.height == 0) {
|
|
548
|
-
return result;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
float totalTopRadius = baseRadius.topLeft + baseRadius.topRight;
|
|
552
|
-
float totalRightRadius = baseRadius.topRight + baseRadius.bottomRight;
|
|
553
|
-
float totalBottomRadius = baseRadius.bottomRight + baseRadius.bottomLeft;
|
|
554
|
-
float totalLeftRadius = baseRadius.bottomLeft + baseRadius.topLeft;
|
|
555
|
-
|
|
556
|
-
float maxHorizontalRadius = std::max(totalTopRadius, totalBottomRadius);
|
|
557
|
-
float maxVerticalRadius = std::max(totalLeftRadius, totalRightRadius);
|
|
558
|
-
|
|
559
|
-
double totalWidth = inset.left + inset.right + pathSize.width;
|
|
560
|
-
double totalHeight = inset.top + inset.bottom + pathSize.height;
|
|
561
|
-
|
|
562
|
-
float scaleHoriz = static_cast<float>(maxHorizontalRadius / totalWidth);
|
|
563
|
-
float scaleVert = static_cast<float>(maxVerticalRadius / totalHeight);
|
|
564
|
-
|
|
565
|
-
float maxScale = std::max(1.0f, std::max(scaleHoriz, scaleVert));
|
|
566
|
-
|
|
567
|
-
result.topLeftRadiusX = std::max(0.0f, baseRadius.topLeft / maxScale - inset.left);
|
|
568
|
-
result.topLeftRadiusY = std::max(0.0f, baseRadius.topLeft / maxScale - inset.top);
|
|
569
|
-
result.topRightRadiusX = std::max(0.0f, baseRadius.topRight / maxScale - inset.right);
|
|
570
|
-
result.topRightRadiusY = std::max(0.0f, baseRadius.topRight / maxScale - inset.top);
|
|
571
|
-
result.bottomRightRadiusX = std::max(0.0f, baseRadius.bottomRight / maxScale - inset.right);
|
|
572
|
-
result.bottomRightRadiusY = std::max(0.0f, baseRadius.bottomRight / maxScale - inset.bottom);
|
|
573
|
-
result.bottomLeftRadiusX = std::max(0.0f, baseRadius.bottomLeft / maxScale - inset.left);
|
|
574
|
-
result.bottomLeftRadiusY = std::max(0.0f, baseRadius.bottomLeft / maxScale - inset.bottom);
|
|
575
|
-
|
|
576
|
-
return result;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
static winrt::com_ptr<ID2D1PathGeometry> GenerateRoundedRectPathGeometry(
|
|
580
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
|
|
581
|
-
const facebook::react::RectangleCorners<float> &baseRadius,
|
|
582
|
-
const facebook::react::RectangleEdges<float> &inset,
|
|
583
|
-
const facebook::react::RectangleEdges<float> &rectPathGeometry) noexcept {
|
|
584
|
-
RoundedPathParameters params = GenerateRoundedPathParameters(
|
|
585
|
-
baseRadius,
|
|
586
|
-
inset,
|
|
587
|
-
{rectPathGeometry.right - rectPathGeometry.left, rectPathGeometry.bottom - rectPathGeometry.top});
|
|
588
|
-
|
|
589
|
-
return GenerateRoundedRectPathGeometry(compContext, params, rectPathGeometry);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
void DrawShape(
|
|
593
|
-
ID2D1RenderTarget *pRT,
|
|
594
|
-
const D2D1_RECT_F &rect,
|
|
595
|
-
ID2D1Brush *brush,
|
|
596
|
-
FLOAT strokeWidth,
|
|
597
|
-
ID2D1StrokeStyle *strokeStyle) {
|
|
598
|
-
pRT->DrawRectangle(rect, brush, strokeWidth, strokeStyle);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
void DrawShape(ID2D1RenderTarget *pRT, ID2D1GeometryGroup &geometry, ID2D1Brush *brush, FLOAT, ID2D1StrokeStyle *) {
|
|
602
|
-
pRT->FillGeometry(&geometry, brush);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
void DrawShape(
|
|
606
|
-
ID2D1RenderTarget *pRT,
|
|
607
|
-
ID2D1PathGeometry &geometry,
|
|
608
|
-
ID2D1Brush *brush,
|
|
609
|
-
FLOAT strokeWidth,
|
|
610
|
-
ID2D1StrokeStyle *strokeStyle) {
|
|
611
|
-
pRT->DrawGeometry(&geometry, brush, strokeWidth, strokeStyle);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
template <typename TShape>
|
|
615
|
-
void SetBorderLayerPropertiesCommon(
|
|
616
|
-
winrt::Microsoft::ReactNative::Composition::implementation::Theme *theme,
|
|
617
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
|
|
618
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual &layer,
|
|
619
|
-
TShape &shape,
|
|
620
|
-
winrt::com_ptr<::Microsoft::ReactNative::Composition::Experimental::ICompositionDrawingSurfaceInterop>
|
|
621
|
-
&borderTexture,
|
|
622
|
-
const D2D1_RECT_F &textureRect,
|
|
623
|
-
facebook::react::Point anchorPoint,
|
|
624
|
-
facebook::react::Point anchorOffset,
|
|
625
|
-
winrt::Windows::Foundation::Numerics::float2 size,
|
|
626
|
-
winrt::Windows::Foundation::Numerics::float2 relativeSizeAdjustment,
|
|
627
|
-
FLOAT strokeWidth,
|
|
628
|
-
const facebook::react::SharedColor &borderColor,
|
|
629
|
-
facebook::react::BorderStyle borderStyle) {
|
|
630
|
-
layer.Offset({anchorOffset.x, anchorOffset.y, 0}, {anchorPoint.x, anchorPoint.y, 0});
|
|
631
|
-
layer.RelativeSizeWithOffset(size, relativeSizeAdjustment);
|
|
632
|
-
layer.as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(nullptr);
|
|
633
|
-
|
|
634
|
-
if ((textureRect.right - textureRect.left) <= 0 || (textureRect.bottom - textureRect.top) <= 0)
|
|
635
|
-
return;
|
|
636
|
-
|
|
637
|
-
auto surface = compContext.CreateDrawingSurfaceBrush(
|
|
638
|
-
{(textureRect.right - textureRect.left), (textureRect.bottom - textureRect.top)},
|
|
639
|
-
winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized,
|
|
640
|
-
winrt::Windows::Graphics::DirectX::DirectXAlphaMode::Premultiplied);
|
|
641
|
-
surface.as(borderTexture);
|
|
642
|
-
|
|
643
|
-
layer.Brush(surface);
|
|
644
|
-
|
|
645
|
-
POINT offset;
|
|
646
|
-
::Microsoft::ReactNative::Composition::AutoDrawDrawingSurface autoDraw(
|
|
647
|
-
surface, 1.0f /* We have already done the dpi scaling */, &offset);
|
|
648
|
-
if (auto pRT = autoDraw.GetRenderTarget()) {
|
|
649
|
-
// Clear with transparency
|
|
650
|
-
pRT->Clear();
|
|
651
|
-
|
|
652
|
-
if (!facebook::react::isColorMeaningful(borderColor)) {
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
winrt::com_ptr<ID2D1Factory> spFactory;
|
|
657
|
-
pRT->GetFactory(spFactory.put());
|
|
658
|
-
assert(spFactory);
|
|
659
|
-
if (spFactory == nullptr)
|
|
660
|
-
return;
|
|
661
|
-
|
|
662
|
-
winrt::com_ptr<ID2D1SolidColorBrush> spBorderBrush;
|
|
663
|
-
pRT->CreateSolidColorBrush(theme->D2DColor(*borderColor), spBorderBrush.put());
|
|
664
|
-
assert(spBorderBrush);
|
|
665
|
-
if (spBorderBrush == nullptr)
|
|
666
|
-
return;
|
|
667
|
-
|
|
668
|
-
winrt::com_ptr<ID2D1StrokeStyle> spStrokeStyle;
|
|
669
|
-
|
|
670
|
-
enum class BorderStyle { Solid, Dotted, Dashed };
|
|
671
|
-
|
|
672
|
-
if (borderStyle == facebook::react::BorderStyle::Dotted || borderStyle == facebook::react::BorderStyle::Dashed) {
|
|
673
|
-
const auto capStyle =
|
|
674
|
-
borderStyle == facebook::react::BorderStyle::Dashed ? D2D1_CAP_STYLE_FLAT : D2D1_CAP_STYLE_ROUND;
|
|
675
|
-
const auto strokeStyleProps = D2D1::StrokeStyleProperties(
|
|
676
|
-
capStyle,
|
|
677
|
-
capStyle,
|
|
678
|
-
capStyle,
|
|
679
|
-
D2D1_LINE_JOIN_MITER,
|
|
680
|
-
10.0f,
|
|
681
|
-
borderStyle == facebook::react::BorderStyle::Dashed ? D2D1_DASH_STYLE_DASH : D2D1_DASH_STYLE_DOT,
|
|
682
|
-
0.0f);
|
|
683
|
-
spFactory->CreateStrokeStyle(&strokeStyleProps, nullptr, 0, spStrokeStyle.put());
|
|
684
|
-
}
|
|
685
|
-
D2D1::Matrix3x2F originalTransform;
|
|
686
|
-
D2D1::Matrix3x2F translationTransform =
|
|
687
|
-
D2D1::Matrix3x2F::Translation(-textureRect.left + offset.x, -textureRect.top + offset.y);
|
|
688
|
-
|
|
689
|
-
pRT->GetTransform(&originalTransform);
|
|
690
|
-
translationTransform = originalTransform * translationTransform;
|
|
691
|
-
|
|
692
|
-
pRT->SetTransform(translationTransform);
|
|
693
|
-
|
|
694
|
-
DrawShape(pRT, shape, spBorderBrush.get(), strokeWidth, spStrokeStyle.get());
|
|
695
|
-
|
|
696
|
-
pRT->SetTransform(originalTransform);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
template <typename TShape>
|
|
701
|
-
void SetBorderLayerProperties(
|
|
702
|
-
winrt::Microsoft::ReactNative::Composition::implementation::Theme *theme,
|
|
703
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
|
|
704
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual &layer,
|
|
705
|
-
TShape &shape,
|
|
706
|
-
winrt::com_ptr<::Microsoft::ReactNative::Composition::Experimental::ICompositionDrawingSurfaceInterop>
|
|
707
|
-
&borderTexture,
|
|
708
|
-
const D2D1_RECT_F &textureRect,
|
|
709
|
-
facebook::react::Point anchorPoint,
|
|
710
|
-
facebook::react::Point anchorOffset,
|
|
711
|
-
winrt::Windows::Foundation::Numerics::float2 size,
|
|
712
|
-
winrt::Windows::Foundation::Numerics::float2 relativeSizeAdjustment,
|
|
713
|
-
FLOAT strokeWidth,
|
|
714
|
-
const facebook::react::SharedColor &borderColor,
|
|
715
|
-
facebook::react::BorderStyle borderStyle) {
|
|
716
|
-
if constexpr (!std::is_base_of_v<ID2D1GeometryGroup, TShape>) {
|
|
717
|
-
SetBorderLayerPropertiesCommon(
|
|
718
|
-
theme,
|
|
719
|
-
compContext,
|
|
720
|
-
layer,
|
|
721
|
-
shape,
|
|
722
|
-
borderTexture,
|
|
723
|
-
textureRect,
|
|
724
|
-
anchorPoint,
|
|
725
|
-
anchorOffset,
|
|
726
|
-
size,
|
|
727
|
-
relativeSizeAdjustment,
|
|
728
|
-
strokeWidth,
|
|
729
|
-
borderColor,
|
|
730
|
-
borderStyle);
|
|
731
|
-
} else {
|
|
732
|
-
// if (VisualVersion::IsUseWinCompClippingRegionEnabled())
|
|
733
|
-
{
|
|
734
|
-
layer.Offset({anchorOffset.x, anchorOffset.y, 0}, {anchorPoint.x, anchorPoint.y, 0});
|
|
735
|
-
layer.RelativeSizeWithOffset(
|
|
736
|
-
{textureRect.right - textureRect.left, textureRect.bottom - textureRect.top}, {0.0f, 0.0f});
|
|
737
|
-
|
|
738
|
-
layer.Brush(theme->Brush(*borderColor));
|
|
739
|
-
|
|
740
|
-
winrt::com_ptr<ID2D1Factory1> spD2dFactory;
|
|
741
|
-
compContext.as<::Microsoft::ReactNative::Composition::Experimental::ICompositionContextInterop>()->D2DFactory(
|
|
742
|
-
spD2dFactory.put());
|
|
743
|
-
|
|
744
|
-
winrt::com_ptr<ID2D1TransformedGeometry> transformedShape;
|
|
745
|
-
D2D1::Matrix3x2F translationTransform = D2D1::Matrix3x2F::Translation(-textureRect.left, -textureRect.top);
|
|
746
|
-
winrt::check_hresult(
|
|
747
|
-
spD2dFactory->CreateTransformedGeometry(&shape, &translationTransform, transformedShape.put()));
|
|
748
|
-
|
|
749
|
-
layer.as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(
|
|
750
|
-
transformedShape.get());
|
|
751
|
-
}
|
|
752
|
-
/*
|
|
753
|
-
else
|
|
754
|
-
{
|
|
755
|
-
SetBorderLayerPropertiesCommon(theme, comContext, layer, shape, borderTexture, textureRect,
|
|
756
|
-
anchorPoint, anchorOffset, strokeWidth, borderColor, borderStyle);
|
|
757
|
-
}
|
|
758
|
-
*/
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
namespace AnchorPosition {
|
|
763
|
-
const float Left = 0.0;
|
|
764
|
-
const float Center = 0.5;
|
|
765
|
-
const float Right = 1.0;
|
|
766
|
-
const float Top = 0.0;
|
|
767
|
-
const float Bottom = 1.0;
|
|
768
|
-
} // namespace AnchorPosition
|
|
769
|
-
|
|
770
|
-
template <typename TShape>
|
|
771
|
-
void DrawAllBorderLayers(
|
|
772
|
-
winrt::Microsoft::ReactNative::Composition::implementation::Theme *theme,
|
|
773
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
|
|
774
|
-
std::array<
|
|
775
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual,
|
|
776
|
-
ComponentView::SpecialBorderLayerCount> &spBorderLayers,
|
|
777
|
-
TShape &shape,
|
|
778
|
-
const facebook::react::BorderWidths &borderWidths,
|
|
779
|
-
const facebook::react::BorderRadii &borderRadii,
|
|
780
|
-
float textureWidth,
|
|
781
|
-
float textureHeight,
|
|
782
|
-
const facebook::react::BorderColors &borderColors,
|
|
783
|
-
facebook::react::BorderStyle borderStyle) {
|
|
784
|
-
// Now that we've drawn our nice border in one layer, split it into its component layers
|
|
785
|
-
winrt::com_ptr<::Microsoft::ReactNative::Composition::Experimental::ICompositionDrawingSurfaceInterop>
|
|
786
|
-
spTextures[ComponentView::SpecialBorderLayerCount];
|
|
787
|
-
|
|
788
|
-
// Set component border properties
|
|
789
|
-
// Top Left Corner
|
|
790
|
-
SetBorderLayerProperties(
|
|
791
|
-
theme,
|
|
792
|
-
compContext,
|
|
793
|
-
spBorderLayers[0],
|
|
794
|
-
shape,
|
|
795
|
-
spTextures[0], // Target Layer, Source Texture, Target Texture
|
|
796
|
-
{0,
|
|
797
|
-
0,
|
|
798
|
-
borderRadii.topLeft + borderWidths.left,
|
|
799
|
-
borderRadii.topLeft + borderWidths.top}, // Texture Left, Top, Width, Height
|
|
800
|
-
{AnchorPosition::Left, AnchorPosition::Top}, // Layer Anchor Point
|
|
801
|
-
{0, 0}, // Layer Anchor Offset
|
|
802
|
-
{borderRadii.topLeft + borderWidths.left, borderRadii.topLeft + borderWidths.top}, // size
|
|
803
|
-
{0.0f, 0.0f}, // relativeSize
|
|
804
|
-
std::max(borderWidths.left, borderWidths.top),
|
|
805
|
-
borderColors.left ? borderColors.left : borderColors.top,
|
|
806
|
-
borderStyle);
|
|
807
|
-
|
|
808
|
-
// Top Edge Border
|
|
809
|
-
SetBorderLayerProperties(
|
|
810
|
-
theme,
|
|
811
|
-
compContext,
|
|
812
|
-
spBorderLayers[1],
|
|
813
|
-
shape,
|
|
814
|
-
spTextures[1],
|
|
815
|
-
{borderRadii.topLeft + borderWidths.left,
|
|
816
|
-
0,
|
|
817
|
-
textureWidth - (borderRadii.topRight + borderWidths.right),
|
|
818
|
-
borderWidths.top},
|
|
819
|
-
{AnchorPosition::Left, AnchorPosition::Top},
|
|
820
|
-
{borderRadii.topLeft + borderWidths.left, 0},
|
|
821
|
-
{-(borderRadii.topLeft + borderWidths.left + borderRadii.topRight + borderWidths.right),
|
|
822
|
-
borderWidths.top}, // size
|
|
823
|
-
{1.0f, 0.0f}, // relativeSize
|
|
824
|
-
borderWidths.top,
|
|
825
|
-
borderColors.top,
|
|
826
|
-
borderStyle);
|
|
827
|
-
|
|
828
|
-
// Top Right Corner Border
|
|
829
|
-
SetBorderLayerProperties(
|
|
830
|
-
theme,
|
|
831
|
-
compContext,
|
|
832
|
-
spBorderLayers[2],
|
|
833
|
-
shape,
|
|
834
|
-
spTextures[2],
|
|
835
|
-
{textureWidth - (borderRadii.topRight + borderWidths.right),
|
|
836
|
-
0,
|
|
837
|
-
textureWidth,
|
|
838
|
-
borderRadii.topRight + borderWidths.top},
|
|
839
|
-
{AnchorPosition::Right, AnchorPosition::Top},
|
|
840
|
-
{-(borderRadii.topRight + borderWidths.right), 0},
|
|
841
|
-
{borderRadii.topRight + borderWidths.right, borderRadii.topRight + borderWidths.top},
|
|
842
|
-
{0.0f, 0.0f},
|
|
843
|
-
std::max(borderWidths.right, borderWidths.top),
|
|
844
|
-
borderColors.right ? borderColors.right : borderColors.top,
|
|
845
|
-
borderStyle);
|
|
846
|
-
|
|
847
|
-
// Right Edge Border
|
|
848
|
-
SetBorderLayerProperties(
|
|
849
|
-
theme,
|
|
850
|
-
compContext,
|
|
851
|
-
spBorderLayers[3],
|
|
852
|
-
shape,
|
|
853
|
-
spTextures[3],
|
|
854
|
-
{textureWidth - borderWidths.right,
|
|
855
|
-
borderWidths.top + borderRadii.topRight,
|
|
856
|
-
textureWidth,
|
|
857
|
-
textureHeight - (borderWidths.bottom + borderRadii.bottomRight)},
|
|
858
|
-
{AnchorPosition::Right, AnchorPosition::Top},
|
|
859
|
-
{-borderWidths.right, borderWidths.top + borderRadii.topRight},
|
|
860
|
-
{borderWidths.right,
|
|
861
|
-
-(borderWidths.top + borderRadii.topRight + borderWidths.bottom + borderRadii.bottomRight)}, // size
|
|
862
|
-
{0.0f, 1.0f},
|
|
863
|
-
borderWidths.right,
|
|
864
|
-
borderColors.right,
|
|
865
|
-
borderStyle);
|
|
866
|
-
|
|
867
|
-
// Bottom Right Corner Border
|
|
868
|
-
SetBorderLayerProperties(
|
|
869
|
-
theme,
|
|
870
|
-
compContext,
|
|
871
|
-
spBorderLayers[4],
|
|
872
|
-
shape,
|
|
873
|
-
spTextures[4],
|
|
874
|
-
{textureWidth - (borderWidths.right + borderRadii.bottomRight),
|
|
875
|
-
textureHeight - (borderWidths.bottom + borderRadii.bottomRight),
|
|
876
|
-
textureWidth,
|
|
877
|
-
textureHeight},
|
|
878
|
-
{AnchorPosition::Right, AnchorPosition::Bottom},
|
|
879
|
-
{-(borderWidths.right + borderRadii.bottomRight), -(borderWidths.bottom + borderRadii.bottomRight)},
|
|
880
|
-
{borderWidths.right + borderRadii.bottomRight, borderWidths.bottom + borderRadii.bottomRight},
|
|
881
|
-
{0, 0},
|
|
882
|
-
std::max(borderWidths.right, borderWidths.bottom),
|
|
883
|
-
borderColors.right ? borderColors.right : borderColors.bottom,
|
|
884
|
-
borderStyle);
|
|
885
|
-
|
|
886
|
-
// Bottom Edge Border
|
|
887
|
-
SetBorderLayerProperties(
|
|
888
|
-
theme,
|
|
889
|
-
compContext,
|
|
890
|
-
spBorderLayers[5],
|
|
891
|
-
shape,
|
|
892
|
-
spTextures[5],
|
|
893
|
-
{borderWidths.left + borderRadii.bottomLeft,
|
|
894
|
-
textureHeight - borderWidths.bottom,
|
|
895
|
-
textureWidth - (borderWidths.right + borderRadii.bottomRight),
|
|
896
|
-
textureHeight},
|
|
897
|
-
{AnchorPosition::Left, AnchorPosition::Bottom},
|
|
898
|
-
{borderWidths.left + borderRadii.bottomLeft, -borderWidths.bottom},
|
|
899
|
-
{-(borderWidths.right + borderRadii.bottomLeft + borderWidths.left + borderRadii.bottomRight),
|
|
900
|
-
borderWidths.bottom},
|
|
901
|
-
{1.0f, 0.0f},
|
|
902
|
-
borderWidths.bottom,
|
|
903
|
-
borderColors.bottom,
|
|
904
|
-
borderStyle);
|
|
905
|
-
|
|
906
|
-
// Bottom Left Corner Border
|
|
907
|
-
SetBorderLayerProperties(
|
|
908
|
-
theme,
|
|
909
|
-
compContext,
|
|
910
|
-
spBorderLayers[6],
|
|
911
|
-
shape,
|
|
912
|
-
spTextures[6],
|
|
913
|
-
{0,
|
|
914
|
-
textureHeight - (borderWidths.bottom + borderRadii.bottomLeft),
|
|
915
|
-
borderWidths.left + borderRadii.bottomLeft,
|
|
916
|
-
textureHeight},
|
|
917
|
-
{AnchorPosition::Left, AnchorPosition::Bottom},
|
|
918
|
-
{0, -(borderWidths.bottom + borderRadii.bottomLeft)},
|
|
919
|
-
{borderWidths.left + borderRadii.bottomLeft, borderWidths.bottom + borderRadii.bottomLeft},
|
|
920
|
-
{0, 0},
|
|
921
|
-
std::max(borderWidths.left, borderWidths.bottom),
|
|
922
|
-
borderColors.left ? borderColors.left : borderColors.bottom,
|
|
923
|
-
borderStyle);
|
|
924
|
-
|
|
925
|
-
// Left Edge Border
|
|
926
|
-
SetBorderLayerProperties(
|
|
927
|
-
theme,
|
|
928
|
-
compContext,
|
|
929
|
-
spBorderLayers[7],
|
|
930
|
-
shape,
|
|
931
|
-
spTextures[7],
|
|
932
|
-
{0,
|
|
933
|
-
borderWidths.top + borderRadii.topLeft,
|
|
934
|
-
borderWidths.left,
|
|
935
|
-
textureHeight - (borderWidths.bottom + borderRadii.bottomLeft)},
|
|
936
|
-
{AnchorPosition::Left, AnchorPosition::Top},
|
|
937
|
-
{0, borderWidths.top + borderRadii.topLeft},
|
|
938
|
-
{borderWidths.left, -(borderWidths.top + borderRadii.topLeft + borderWidths.bottom + borderRadii.bottomLeft)},
|
|
939
|
-
{0, 1},
|
|
940
|
-
borderWidths.left,
|
|
941
|
-
borderColors.left,
|
|
942
|
-
borderStyle);
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
winrt::com_ptr<ID2D1GeometryGroup> GetGeometryForRoundedBorder(
|
|
946
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
|
|
947
|
-
const facebook::react::RectangleCorners<float> &radius,
|
|
948
|
-
const facebook::react::RectangleEdges<float> &inset,
|
|
949
|
-
const facebook::react::RectangleEdges<float> &thickness,
|
|
950
|
-
const facebook::react::RectangleEdges<float> &rectPathGeometry) noexcept {
|
|
951
|
-
winrt::com_ptr<ID2D1PathGeometry> outerPathGeometry =
|
|
952
|
-
GenerateRoundedRectPathGeometry(compContext, radius, inset, rectPathGeometry);
|
|
953
|
-
|
|
954
|
-
if (outerPathGeometry == nullptr) {
|
|
955
|
-
assert(false);
|
|
956
|
-
return nullptr;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
facebook::react::RectangleEdges<float> rectInnerPathGeometry = {
|
|
960
|
-
rectPathGeometry.left + thickness.left,
|
|
961
|
-
rectPathGeometry.top + thickness.top,
|
|
962
|
-
rectPathGeometry.right - thickness.right,
|
|
963
|
-
rectPathGeometry.bottom - thickness.bottom};
|
|
964
|
-
|
|
965
|
-
// Total thickness is larger than original element size.
|
|
966
|
-
// Clamp inner rect to have a width/height of 0, but placed such that the ratio of side-thicknesses is respected.
|
|
967
|
-
// We need to respect this ratio so that any animations work properly.
|
|
968
|
-
|
|
969
|
-
if (rectInnerPathGeometry.left > rectInnerPathGeometry.right) {
|
|
970
|
-
float leftRatio = thickness.left / (thickness.left + thickness.right);
|
|
971
|
-
auto x = std::floor(rectPathGeometry.left + ((rectPathGeometry.right - rectPathGeometry.left) * leftRatio));
|
|
972
|
-
rectInnerPathGeometry.left = x;
|
|
973
|
-
rectInnerPathGeometry.right = x;
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
if (rectInnerPathGeometry.top > rectInnerPathGeometry.bottom) {
|
|
977
|
-
float topRatio = thickness.top / (thickness.top + thickness.bottom);
|
|
978
|
-
auto y = rectPathGeometry.top + std::floor((rectPathGeometry.top - rectPathGeometry.bottom) * topRatio);
|
|
979
|
-
rectInnerPathGeometry.top = y;
|
|
980
|
-
rectInnerPathGeometry.bottom = y;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
facebook::react::RectangleEdges<float> innerInset = {
|
|
984
|
-
inset.left + thickness.left,
|
|
985
|
-
inset.top + thickness.top,
|
|
986
|
-
inset.right + thickness.right,
|
|
987
|
-
inset.bottom + thickness.bottom};
|
|
988
|
-
|
|
989
|
-
winrt::com_ptr<ID2D1PathGeometry> innerPathGeometry =
|
|
990
|
-
GenerateRoundedRectPathGeometry(compContext, radius, innerInset, rectInnerPathGeometry);
|
|
991
|
-
|
|
992
|
-
if (innerPathGeometry == nullptr) {
|
|
993
|
-
assert(false); // Failed to create inner pathGeometry for rounded border
|
|
994
|
-
return nullptr;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
ID2D1Geometry *ppGeometries[] = {outerPathGeometry.get(), innerPathGeometry.get()};
|
|
998
|
-
winrt::com_ptr<ID2D1Factory1> spD2dFactory;
|
|
999
|
-
compContext.as<::Microsoft::ReactNative::Composition::Experimental::ICompositionContextInterop>()->D2DFactory(
|
|
1000
|
-
spD2dFactory.put());
|
|
1001
|
-
|
|
1002
|
-
winrt::com_ptr<ID2D1GeometryGroup> geometryGroup = nullptr;
|
|
1003
|
-
// Create a geometry group.
|
|
1004
|
-
HRESULT hr = spD2dFactory->CreateGeometryGroup(
|
|
1005
|
-
D2D1_FILL_MODE_ALTERNATE, ppGeometries, ARRAYSIZE(ppGeometries), geometryGroup.put());
|
|
1006
|
-
|
|
1007
|
-
if (SUCCEEDED(hr)) {
|
|
1008
|
-
return geometryGroup;
|
|
1009
|
-
}
|
|
1010
|
-
return nullptr;
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
// We don't want half pixel borders, or border radii - they lead to blurry borders
|
|
1014
|
-
// Also apply scale factor to the radii at this point
|
|
1015
|
-
void pixelRoundBorderRadii(facebook::react::BorderRadii &borderRadii, float scaleFactor) noexcept {
|
|
1016
|
-
// Always round radii down to avoid spikey circles
|
|
1017
|
-
borderRadii.topLeft = std::floor(borderRadii.topLeft * scaleFactor);
|
|
1018
|
-
borderRadii.topRight = std::floor(borderRadii.topRight * scaleFactor);
|
|
1019
|
-
borderRadii.bottomLeft = std::floor(borderRadii.bottomLeft * scaleFactor);
|
|
1020
|
-
borderRadii.bottomRight = std::floor(borderRadii.bottomRight * scaleFactor);
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
void scaleAndPixelRoundBorderWidths(
|
|
1024
|
-
facebook::react::LayoutMetrics const &layoutMetrics,
|
|
1025
|
-
facebook::react::BorderMetrics &borderMetrics,
|
|
1026
|
-
float scaleFactor) noexcept {
|
|
1027
|
-
borderMetrics.borderWidths.left = (borderMetrics.borderWidths.left == 0)
|
|
1028
|
-
? 0.f
|
|
1029
|
-
: std::max(1.f, std::round(borderMetrics.borderWidths.left * scaleFactor));
|
|
1030
|
-
borderMetrics.borderWidths.top = (borderMetrics.borderWidths.top == 0)
|
|
1031
|
-
? 0.f
|
|
1032
|
-
: std::max(1.f, std::round(borderMetrics.borderWidths.top * scaleFactor));
|
|
1033
|
-
borderMetrics.borderWidths.right = (borderMetrics.borderWidths.right == 0)
|
|
1034
|
-
? 0.f
|
|
1035
|
-
: std::max(1.f, std::round(borderMetrics.borderWidths.right * scaleFactor));
|
|
1036
|
-
borderMetrics.borderWidths.bottom = (borderMetrics.borderWidths.bottom == 0)
|
|
1037
|
-
? 0.f
|
|
1038
|
-
: std::max(1.f, std::round(borderMetrics.borderWidths.bottom * scaleFactor));
|
|
1039
|
-
|
|
1040
|
-
// If we rounded both sides of the borderWidths up, we may have made the borderWidths larger than the total
|
|
1041
|
-
if (layoutMetrics.frame.size.width * scaleFactor <
|
|
1042
|
-
(borderMetrics.borderWidths.left + borderMetrics.borderWidths.right)) {
|
|
1043
|
-
borderMetrics.borderWidths.right--;
|
|
1044
|
-
}
|
|
1045
|
-
if (layoutMetrics.frame.size.height * scaleFactor <
|
|
1046
|
-
(borderMetrics.borderWidths.top + borderMetrics.borderWidths.bottom)) {
|
|
1047
|
-
borderMetrics.borderWidths.bottom--;
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
// react-native uses black as a default color when none is specified.
|
|
1052
|
-
void assignDefaultBlackBorders(facebook::react::BorderMetrics &borderMetrics) noexcept {
|
|
1053
|
-
if (!borderMetrics.borderColors.left) {
|
|
1054
|
-
borderMetrics.borderColors.left = facebook::react::blackColor();
|
|
1055
|
-
}
|
|
1056
|
-
if (!borderMetrics.borderColors.top) {
|
|
1057
|
-
borderMetrics.borderColors.top = facebook::react::blackColor();
|
|
1058
|
-
}
|
|
1059
|
-
if (!borderMetrics.borderColors.right) {
|
|
1060
|
-
borderMetrics.borderColors.right = facebook::react::blackColor();
|
|
1061
|
-
}
|
|
1062
|
-
if (!borderMetrics.borderColors.bottom) {
|
|
1063
|
-
borderMetrics.borderColors.bottom = facebook::react::blackColor();
|
|
1064
|
-
}
|
|
458
|
+
winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ComponentView::OuterVisual() const noexcept {
|
|
459
|
+
return m_outerVisual ? m_outerVisual : Visual();
|
|
1065
460
|
}
|
|
1066
461
|
|
|
1067
|
-
facebook::react::
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
462
|
+
facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) const noexcept {
|
|
463
|
+
facebook::react::LayoutMetrics layoutMetrics = m_layoutMetrics;
|
|
464
|
+
layoutMetrics.frame.origin.x -= FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
|
|
465
|
+
layoutMetrics.frame.origin.y -= FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
|
|
466
|
+
layoutMetrics.frame.size.height += FOCUS_VISUAL_WIDTH * (inner ? 2 : 4);
|
|
467
|
+
layoutMetrics.frame.size.width += FOCUS_VISUAL_WIDTH * (inner ? 2 : 4);
|
|
468
|
+
return layoutMetrics;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
facebook::react::BorderMetrics ComponentView::focusBorderMetrics(
|
|
472
|
+
bool inner,
|
|
473
|
+
const facebook::react::LayoutMetrics &layoutMetrics) const noexcept {
|
|
474
|
+
facebook::react::BorderMetrics metrics = BorderPrimitive::resolveAndAlignBorderMetrics(layoutMetrics, *viewProps());
|
|
475
|
+
facebook::react::Color innerColor;
|
|
476
|
+
innerColor.m_color = {1, 0, 0, 0};
|
|
477
|
+
innerColor.m_platformColor.push_back(inner ? "FocusVisualSecondary" : "FocusVisualPrimary");
|
|
478
|
+
metrics.borderColors.bottom = metrics.borderColors.left = metrics.borderColors.right = metrics.borderColors.top =
|
|
479
|
+
innerColor;
|
|
480
|
+
if (metrics.borderRadii.bottomLeft != 0)
|
|
481
|
+
metrics.borderRadii.bottomLeft += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
|
|
482
|
+
if (metrics.borderRadii.bottomRight != 0)
|
|
483
|
+
metrics.borderRadii.bottomRight += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
|
|
484
|
+
if (metrics.borderRadii.topLeft != 0)
|
|
485
|
+
metrics.borderRadii.topLeft += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
|
|
486
|
+
if (metrics.borderRadii.topRight != 0)
|
|
487
|
+
metrics.borderRadii.topRight += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2);
|
|
488
|
+
|
|
489
|
+
metrics.borderStyles.bottom = metrics.borderStyles.left = metrics.borderStyles.right = metrics.borderStyles.top =
|
|
490
|
+
facebook::react::BorderStyle::Solid;
|
|
491
|
+
metrics.borderWidths.bottom = metrics.borderWidths.left = metrics.borderWidths.right = metrics.borderWidths.top =
|
|
492
|
+
FOCUS_VISUAL_WIDTH * layoutMetrics.pointScaleFactor;
|
|
493
|
+
return metrics;
|
|
1076
494
|
}
|
|
1077
495
|
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
bool hasMeaningfulColor =
|
|
1089
|
-
!borderMetrics.borderColors.isUniform() || !facebook::react::isColorMeaningful(borderMetrics.borderColors.left);
|
|
1090
|
-
bool hasMeaningfulWidth = !borderMetrics.borderWidths.isUniform() || (borderMetrics.borderWidths.left != 0);
|
|
1091
|
-
if (!hasMeaningfulColor && !hasMeaningfulWidth) {
|
|
1092
|
-
return false;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// Create the special border layers if they don't exist yet
|
|
1096
|
-
if (!spBorderVisuals[0]) {
|
|
1097
|
-
for (uint8_t i = 0; i < SpecialBorderLayerCount; i++) {
|
|
1098
|
-
auto visual = m_compContext.CreateSpriteVisual();
|
|
1099
|
-
Visual().InsertAt(visual, i);
|
|
1100
|
-
spBorderVisuals[i] = std::move(visual);
|
|
1101
|
-
m_numBorderVisuals++;
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
float extentWidth = layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor;
|
|
1106
|
-
float extentHeight = layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor;
|
|
1107
|
-
|
|
1108
|
-
if (borderMetrics.borderRadii.topLeft != 0 || borderMetrics.borderRadii.topRight != 0 ||
|
|
1109
|
-
borderMetrics.borderRadii.bottomLeft != 0 || borderMetrics.borderRadii.bottomRight != 0) {
|
|
1110
|
-
if (borderStyle == facebook::react::BorderStyle::Dotted || borderStyle == facebook::react::BorderStyle::Dashed) {
|
|
1111
|
-
// Because in DirectX geometry starts at the center of the stroke, we need to deflate
|
|
1112
|
-
// rectangle by half the stroke width to render correctly.
|
|
1113
|
-
facebook::react::RectangleEdges<float> rectPathGeometry = {
|
|
1114
|
-
borderMetrics.borderWidths.left / 2.0f,
|
|
1115
|
-
borderMetrics.borderWidths.top / 2.0f,
|
|
1116
|
-
extentWidth - borderMetrics.borderWidths.right / 2.0f,
|
|
1117
|
-
extentHeight - borderMetrics.borderWidths.bottom / 2.0f};
|
|
1118
|
-
|
|
1119
|
-
winrt::com_ptr<ID2D1PathGeometry> pathGeometry =
|
|
1120
|
-
GenerateRoundedRectPathGeometry(m_compContext, borderMetrics.borderRadii, {0, 0, 0, 0}, rectPathGeometry);
|
|
1121
|
-
|
|
1122
|
-
if (pathGeometry) {
|
|
1123
|
-
DrawAllBorderLayers(
|
|
1124
|
-
theme,
|
|
1125
|
-
m_compContext,
|
|
1126
|
-
spBorderVisuals,
|
|
1127
|
-
*pathGeometry,
|
|
1128
|
-
borderMetrics.borderWidths,
|
|
1129
|
-
borderMetrics.borderRadii,
|
|
1130
|
-
extentWidth,
|
|
1131
|
-
extentHeight,
|
|
1132
|
-
borderMetrics.borderColors,
|
|
1133
|
-
borderStyle);
|
|
1134
|
-
} else {
|
|
1135
|
-
assert(false);
|
|
496
|
+
void ComponentView::showFocusVisual(bool show) noexcept {
|
|
497
|
+
if ((m_flags & ComponentViewFeatures::FocusVisual) == ComponentViewFeatures::FocusVisual) {
|
|
498
|
+
if (show && !m_showingFocusVisual) {
|
|
499
|
+
m_showingFocusVisual = true;
|
|
500
|
+
|
|
501
|
+
m_focusVisual.IsVisible(true);
|
|
502
|
+
assert(viewProps()->enableFocusRing);
|
|
503
|
+
if (!m_focusInnerPrimitive) {
|
|
504
|
+
m_focusInnerPrimitive = std::make_shared<BorderPrimitive>(*this);
|
|
505
|
+
m_focusVisual.InsertAt(m_focusInnerPrimitive->RootVisual(), 0);
|
|
1136
506
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
}
|
|
1159
|
-
} else {
|
|
1160
|
-
// Because in DirectX geometry starts at the center of the stroke, we need to deflate rectangle by half the stroke
|
|
1161
|
-
// width / height to render correctly.
|
|
1162
|
-
D2D1_RECT_F rectShape{
|
|
1163
|
-
borderMetrics.borderWidths.left / 2.0f,
|
|
1164
|
-
borderMetrics.borderWidths.top / 2.0f,
|
|
1165
|
-
extentWidth - (borderMetrics.borderWidths.right / 2.0f),
|
|
1166
|
-
extentHeight - (borderMetrics.borderWidths.bottom / 2.0f)};
|
|
1167
|
-
DrawAllBorderLayers(
|
|
1168
|
-
theme,
|
|
1169
|
-
m_compContext,
|
|
1170
|
-
spBorderVisuals,
|
|
1171
|
-
rectShape,
|
|
1172
|
-
borderMetrics.borderWidths,
|
|
1173
|
-
borderMetrics.borderRadii,
|
|
1174
|
-
extentWidth,
|
|
1175
|
-
extentHeight,
|
|
1176
|
-
borderMetrics.borderColors,
|
|
1177
|
-
borderStyle);
|
|
1178
|
-
}
|
|
1179
|
-
return true;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
void ComponentView::finalizeBorderUpdates(
|
|
1183
|
-
facebook::react::LayoutMetrics const &layoutMetrics,
|
|
1184
|
-
const facebook::react::ViewProps &viewProps) noexcept {
|
|
1185
|
-
if (!m_needsBorderUpdate || theme()->IsEmpty()) {
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
m_needsBorderUpdate = false;
|
|
1190
|
-
auto spBorderLayers = FindSpecialBorderLayers();
|
|
1191
|
-
|
|
1192
|
-
if (!TryUpdateSpecialBorderLayers(theme(), spBorderLayers, layoutMetrics, viewProps)) {
|
|
1193
|
-
for (auto &spBorderLayer : spBorderLayers) {
|
|
1194
|
-
if (spBorderLayer) {
|
|
1195
|
-
spBorderLayer.as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Brush(nullptr);
|
|
507
|
+
if (!m_focusOuterPrimitive) {
|
|
508
|
+
m_focusOuterPrimitive = std::make_shared<BorderPrimitive>(*this);
|
|
509
|
+
m_focusVisual.InsertAt(m_focusOuterPrimitive->RootVisual(), 0);
|
|
510
|
+
}
|
|
511
|
+
updateFocusLayoutMetrics(m_layoutMetrics);
|
|
512
|
+
auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
|
|
513
|
+
m_focusInnerPrimitive->finalize(innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
|
|
514
|
+
auto outerFocusMetrics = focusLayoutMetrics(false /*inner*/);
|
|
515
|
+
m_focusOuterPrimitive->finalize(outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
|
|
516
|
+
} else if (!show && m_showingFocusVisual) {
|
|
517
|
+
m_showingFocusVisual = false;
|
|
518
|
+
m_focusVisual.IsVisible(false);
|
|
519
|
+
if (m_focusInnerPrimitive) {
|
|
520
|
+
m_focusInnerPrimitive->markNeedsUpdate();
|
|
521
|
+
auto innerFocusMetrics = focusLayoutMetrics(true /*inner*/);
|
|
522
|
+
m_focusInnerPrimitive->finalize(innerFocusMetrics, focusBorderMetrics(true /*inner*/, innerFocusMetrics));
|
|
523
|
+
}
|
|
524
|
+
if (m_focusOuterPrimitive) {
|
|
525
|
+
m_focusOuterPrimitive->markNeedsUpdate();
|
|
526
|
+
auto outerFocusMetrics = focusLayoutMetrics(false /*inner*/);
|
|
527
|
+
m_focusOuterPrimitive->finalize(outerFocusMetrics, focusBorderMetrics(false /*inner*/, outerFocusMetrics));
|
|
1196
528
|
}
|
|
1197
529
|
}
|
|
1198
530
|
}
|
|
1199
531
|
}
|
|
1200
532
|
|
|
1201
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ComponentView::OuterVisual() const noexcept {
|
|
1202
|
-
return m_outerVisual ? m_outerVisual : Visual();
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
void ComponentView::showFocusVisual(bool show) noexcept {
|
|
1206
|
-
if (show) {
|
|
1207
|
-
assert(m_enableFocusVisual);
|
|
1208
|
-
m_focusVisual.IsFocused(true);
|
|
1209
|
-
} else {
|
|
1210
|
-
m_focusVisual.IsFocused(false);
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
void ComponentView::updateBorderProps(
|
|
1215
|
-
const facebook::react::ViewProps &oldViewProps,
|
|
1216
|
-
const facebook::react::ViewProps &newViewProps) noexcept {
|
|
1217
|
-
if (oldViewProps.borderColors != newViewProps.borderColors || oldViewProps.borderRadii != newViewProps.borderRadii ||
|
|
1218
|
-
!(oldViewProps.yogaStyle.border(facebook::yoga::Edge::All) ==
|
|
1219
|
-
newViewProps.yogaStyle.border(facebook::yoga::Edge::All)) ||
|
|
1220
|
-
oldViewProps.borderStyles != newViewProps.borderStyles) {
|
|
1221
|
-
m_needsBorderUpdate = true;
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
m_enableFocusVisual = newViewProps.enableFocusRing;
|
|
1225
|
-
if (!m_enableFocusVisual) {
|
|
1226
|
-
showFocusVisual(false);
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
533
|
void ComponentView::updateShadowProps(
|
|
1231
534
|
const facebook::react::ViewProps &oldViewProps,
|
|
1232
535
|
const facebook::react::ViewProps &newViewProps) noexcept {
|
|
@@ -1240,11 +543,13 @@ void ComponentView::updateShadowProps(
|
|
|
1240
543
|
|
|
1241
544
|
void ComponentView::applyShadowProps(const facebook::react::ViewProps &viewProps) noexcept {
|
|
1242
545
|
auto shadow = m_compContext.CreateDropShadow();
|
|
546
|
+
|
|
1243
547
|
shadow.Offset({viewProps.shadowOffset.width, viewProps.shadowOffset.height, 0});
|
|
1244
548
|
shadow.Opacity(viewProps.shadowOpacity);
|
|
1245
549
|
shadow.BlurRadius(viewProps.shadowRadius);
|
|
1246
550
|
if (viewProps.shadowColor)
|
|
1247
551
|
shadow.Color(theme()->Color(*viewProps.shadowColor));
|
|
552
|
+
|
|
1248
553
|
Visual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(shadow);
|
|
1249
554
|
}
|
|
1250
555
|
|
|
@@ -1357,16 +662,16 @@ void ComponentView::Toggle() noexcept {
|
|
|
1357
662
|
// no-op
|
|
1358
663
|
}
|
|
1359
664
|
|
|
1360
|
-
void ComponentView::
|
|
665
|
+
void ComponentView::updateClippingPath(
|
|
1361
666
|
facebook::react::LayoutMetrics const &layoutMetrics,
|
|
1362
667
|
const facebook::react::ViewProps &viewProps) noexcept {
|
|
1363
|
-
auto borderMetrics = resolveAndAlignBorderMetrics(layoutMetrics, viewProps);
|
|
668
|
+
auto borderMetrics = BorderPrimitive::resolveAndAlignBorderMetrics(layoutMetrics, viewProps);
|
|
1364
669
|
|
|
1365
670
|
if (borderMetrics.borderRadii.topLeft == 0 && borderMetrics.borderRadii.topRight == 0 &&
|
|
1366
671
|
borderMetrics.borderRadii.bottomLeft == 0 && borderMetrics.borderRadii.bottomRight == 0) {
|
|
1367
672
|
Visual().as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(nullptr);
|
|
1368
673
|
} else {
|
|
1369
|
-
winrt::com_ptr<ID2D1PathGeometry> pathGeometry = GenerateRoundedRectPathGeometry(
|
|
674
|
+
winrt::com_ptr<ID2D1PathGeometry> pathGeometry = BorderPrimitive::GenerateRoundedRectPathGeometry(
|
|
1370
675
|
m_compContext,
|
|
1371
676
|
borderMetrics.borderRadii,
|
|
1372
677
|
{0, 0, 0, 0},
|
|
@@ -1378,24 +683,12 @@ void ComponentView::updateBorderLayoutMetrics(
|
|
|
1378
683
|
Visual().as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(
|
|
1379
684
|
pathGeometry.get());
|
|
1380
685
|
}
|
|
1381
|
-
|
|
1382
|
-
if (m_layoutMetrics != layoutMetrics) {
|
|
1383
|
-
m_needsBorderUpdate = true;
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
m_focusVisual.ScaleFactor(layoutMetrics.pointScaleFactor);
|
|
1387
|
-
OuterVisual().Size(
|
|
1388
|
-
{layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
|
|
1389
|
-
layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
|
|
1390
|
-
OuterVisual().Offset({
|
|
1391
|
-
layoutMetrics.frame.origin.x * layoutMetrics.pointScaleFactor,
|
|
1392
|
-
layoutMetrics.frame.origin.y * layoutMetrics.pointScaleFactor,
|
|
1393
|
-
0.0f,
|
|
1394
|
-
});
|
|
1395
686
|
}
|
|
1396
687
|
|
|
1397
688
|
void ComponentView::indexOffsetForBorder(uint32_t &index) const noexcept {
|
|
1398
|
-
|
|
689
|
+
if (m_borderPrimitive) {
|
|
690
|
+
index += m_borderPrimitive->numberOfVisuals();
|
|
691
|
+
}
|
|
1399
692
|
}
|
|
1400
693
|
|
|
1401
694
|
void ComponentView::OnRenderingDeviceLost() noexcept {}
|
|
@@ -1564,6 +857,11 @@ winrt::Microsoft::ReactNative::ComponentView ViewComponentView::Create(
|
|
|
1564
857
|
ViewComponentView::defaultProps(), compContext, tag, reactContext, ComponentViewFeatures::Default);
|
|
1565
858
|
}
|
|
1566
859
|
|
|
860
|
+
winrt::Microsoft::ReactNative::Composition::Experimental::IVisual
|
|
861
|
+
ViewComponentView::VisualToMountChildrenInto() noexcept {
|
|
862
|
+
return Visual();
|
|
863
|
+
}
|
|
864
|
+
|
|
1567
865
|
void ViewComponentView::MountChildComponentView(
|
|
1568
866
|
const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
|
|
1569
867
|
uint32_t index) noexcept {
|
|
@@ -1583,7 +881,7 @@ void ViewComponentView::MountChildComponentView(
|
|
|
1583
881
|
}
|
|
1584
882
|
}
|
|
1585
883
|
}
|
|
1586
|
-
|
|
884
|
+
VisualToMountChildrenInto().InsertAt(compositionChild->OuterVisual(), visualIndex);
|
|
1587
885
|
} else {
|
|
1588
886
|
m_hasNonVisualChildren = true;
|
|
1589
887
|
}
|
|
@@ -1596,7 +894,7 @@ void ViewComponentView::UnmountChildComponentView(
|
|
|
1596
894
|
|
|
1597
895
|
indexOffsetForBorder(index);
|
|
1598
896
|
if (auto compositionChild = childComponentView.try_as<ComponentView>()) {
|
|
1599
|
-
|
|
897
|
+
VisualToMountChildrenInto().Remove(compositionChild->OuterVisual());
|
|
1600
898
|
}
|
|
1601
899
|
}
|
|
1602
900
|
|
|
@@ -1898,6 +1196,9 @@ winrt::Microsoft::ReactNative::ComponentView lastDeepChild(
|
|
|
1898
1196
|
return current;
|
|
1899
1197
|
}
|
|
1900
1198
|
|
|
1199
|
+
// Walks the tree calling the function fn on each node.
|
|
1200
|
+
// If fn returns true, then walkTree stops itterating over the tree, and returns true.
|
|
1201
|
+
// If the tree walk completes without fn returning true, then walkTree returns false.
|
|
1901
1202
|
bool walkTree(
|
|
1902
1203
|
const winrt::Microsoft::ReactNative::ComponentView &view,
|
|
1903
1204
|
bool forward,
|