Skip to content

Commit 2bb6811

Browse files
authored
[Fabric] Add support for PlatformColor (#7801)
* [Fabric] Add support for PlatformColor * format * Change files * winui3 build fix * Add tracking issue for color fork
1 parent 0c62ed7 commit 2bb6811

File tree

14 files changed

+467
-18
lines changed

14 files changed

+467
-18
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[Fabric] Add support for PlatformColor",
4+
"packageName": "react-native-windows",
5+
"email": "30809111+acoates-ms@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Fabric/ActivityIndicatorComponentView.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ void ActivityIndicatorComponentView::updateProps(
4646
}
4747

4848
if (oldActivityProps.color != newActivityProps.color) {
49-
m_element.Foreground(SolidColorBrushFrom(newActivityProps.color));
49+
m_element.Foreground(newActivityProps.color.AsWindowsBrush());
5050
}
5151

5252
m_props = std::static_pointer_cast<facebook::react::ActivityIndicatorViewProps const>(props);

vnext/Microsoft.ReactNative/Fabric/ImageComponentView.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ void ImageComponentView::updateProps(
8787

8888
if (oldImageProps.tintColor != newImageProps.tintColor) {
8989
if (newImageProps.tintColor) {
90-
m_element->TintColor(ColorFromNumber(*(newImageProps.tintColor)));
90+
m_element->TintColor(newImageProps.tintColor.AsWindowsColor());
9191
} else {
9292
m_element->TintColor(winrt::Colors::Transparent());
9393
}

vnext/Microsoft.ReactNative/Fabric/ParagraphComponentView.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ void ParagraphComponentView::updateProps(
4848

4949
if (oldViewProps.textAttributes.foregroundColor != newViewProps.textAttributes.foregroundColor) {
5050
if (newViewProps.textAttributes.foregroundColor)
51-
m_element.Foreground(SolidColorBrushFrom(newViewProps.textAttributes.foregroundColor));
51+
m_element.Foreground(newViewProps.textAttributes.foregroundColor.AsWindowsBrush());
5252
else
5353
m_element.ClearValue(::xaml::Controls::TextBlock::ForegroundProperty());
5454
}

vnext/Microsoft.ReactNative/Fabric/ViewComponentView.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ void ViewComponentView::updateProps(
4848
auto color = *newViewProps.backgroundColor;
4949

5050
if (newViewProps.backgroundColor) {
51-
m_panel.ViewBackground(SolidColorBrushFrom(newViewProps.backgroundColor));
51+
m_panel.ViewBackground(newViewProps.backgroundColor.AsWindowsBrush());
5252
} else {
5353
m_panel.ClearValue(winrt::Microsoft::ReactNative::ViewPanel::ViewBackgroundProperty());
5454
}
5555
}
5656

5757
if (oldViewProps.borderColors != newViewProps.borderColors) {
5858
if (newViewProps.borderColors.all) {
59-
m_panel.BorderBrush(SolidColorBrushFrom(*newViewProps.borderColors.all));
59+
m_panel.BorderBrush(newViewProps.borderColors.all->AsWindowsBrush());
6060
} else {
6161
m_panel.ClearValue(winrt::Microsoft::ReactNative::ViewPanel::BorderBrushProperty());
6262
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#include "Color.h"
5+
#include <Utils/ValueUtils.h>
6+
7+
namespace facebook {
8+
namespace react {
9+
10+
xaml::Media::Brush SharedColor::AsWindowsBrush() const {
11+
if (!m_color)
12+
return nullptr;
13+
if (!m_color->m_platformColor.empty()) {
14+
return Microsoft::ReactNative::BrushFromColorObject(winrt::to_hstring(m_color->m_platformColor));
15+
}
16+
return xaml::Media::SolidColorBrush(m_color->m_color);
17+
}
18+
19+
SharedColor colorFromComponents(ColorComponents components) {
20+
float ratio = 255;
21+
return SharedColor(ui::ColorHelper::FromArgb(
22+
(int)round(components.alpha * ratio) & 0xff,
23+
(int)round(components.red * ratio) & 0xff,
24+
(int)round(components.green * ratio) & 0xff,
25+
(int)round(components.blue * ratio) & 0xff));
26+
}
27+
28+
ColorComponents colorComponentsFromColor(SharedColor sharedColor) {
29+
float ratio = 255;
30+
auto color = sharedColor.AsWindowsColor();
31+
return ColorComponents{
32+
(float)color.R / ratio, (float)color.G / ratio, (float)color.B / ratio, (float)color.A / ratio};
33+
}
34+
35+
SharedColor clearColor() {
36+
static SharedColor color = colorFromComponents(ColorComponents{0, 0, 0, 0});
37+
return color;
38+
}
39+
40+
SharedColor blackColor() {
41+
static SharedColor color = colorFromComponents(ColorComponents{0, 0, 0, 1});
42+
return color;
43+
}
44+
45+
SharedColor whiteColor() {
46+
static SharedColor color = colorFromComponents(ColorComponents{1, 1, 1, 1});
47+
return color;
48+
}
49+
50+
} // namespace react
51+
} // namespace facebook
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// RN has some weird include directory redirections..this forwards the include to the right place
5+
#include <react/renderer/graphics/platform/cxx/react/renderer/graphics/Color.h>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// RN has some weird include directory redirections..this forwards the include to the right place
5+
#include <react/renderer/graphics/platform/cxx/react/renderer/graphics/Float.h>
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <better/map.h>
11+
#include <folly/dynamic.h>
12+
#include <glog/logging.h>
13+
#include <react/debug/react_native_assert.h>
14+
#include <react/renderer/core/RawProps.h>
15+
#include <react/renderer/graphics/Color.h>
16+
#include <react/renderer/graphics/Geometry.h>
17+
18+
namespace facebook {
19+
namespace react {
20+
21+
#pragma mark - Color
22+
23+
inline void fromRawValue(const RawValue &value, SharedColor &result) {
24+
float red = 0;
25+
float green = 0;
26+
float blue = 0;
27+
float alpha = 0;
28+
29+
if (value.hasType<int>()) {
30+
auto argb = (int64_t)value;
31+
auto ratio = 255.f;
32+
alpha = ((argb >> 24) & 0xFF) / ratio;
33+
red = ((argb >> 16) & 0xFF) / ratio;
34+
green = ((argb >> 8) & 0xFF) / ratio;
35+
blue = (argb & 0xFF) / ratio;
36+
} else if (value.hasType<std::vector<float>>()) {
37+
auto items = (std::vector<float>)value;
38+
auto length = items.size();
39+
react_native_assert(length == 3 || length == 4);
40+
red = items.at(0);
41+
green = items.at(1);
42+
blue = items.at(2);
43+
alpha = length == 4 ? items.at(3) : 1.0f;
44+
// [Windows - Add support for windowsBrush colors (PlatformColor)
45+
} else if (value.hasType<better::map<std::string, std::string>>()) {
46+
auto map = (better::map<std::string, std::string>)value;
47+
for (const auto &pair : map) {
48+
if (pair.first == "windowsbrush") {
49+
result = SharedColor(std::string(pair.second));
50+
return;
51+
}
52+
}
53+
}
54+
// Windows]
55+
56+
result = colorFromComponents({red, green, blue, alpha});
57+
}
58+
59+
#ifdef ANDROID
60+
61+
inline folly::dynamic toDynamic(const SharedColor &color) {
62+
ColorComponents components = colorComponentsFromColor(color);
63+
auto ratio = 255.f;
64+
return (
65+
((int)round(components.alpha * ratio) & 0xff) << 24 | ((int)round(components.red * ratio) & 0xff) << 16 |
66+
((int)round(components.green * ratio) & 0xff) << 8 | ((int)round(components.blue * ratio) & 0xff));
67+
}
68+
69+
inline int toMapBuffer(const SharedColor &color) {
70+
ColorComponents components = colorComponentsFromColor(color);
71+
auto ratio = 255.f;
72+
return (
73+
((int)round(components.alpha * ratio) & 0xff) << 24 | ((int)round(components.red * ratio) & 0xff) << 16 |
74+
((int)round(components.green * ratio) & 0xff) << 8 | ((int)round(components.blue * ratio) & 0xff));
75+
}
76+
77+
#endif
78+
79+
inline std::string toString(const SharedColor &value) {
80+
ColorComponents components = colorComponentsFromColor(value);
81+
auto ratio = 255.f;
82+
return "rgba(" + folly::to<std::string>(round(components.red * ratio)) + ", " +
83+
folly::to<std::string>(round(components.green * ratio)) + ", " +
84+
folly::to<std::string>(round(components.blue * ratio)) + ", " +
85+
folly::to<std::string>(round(components.alpha * ratio)) + ")";
86+
}
87+
88+
#pragma mark - Geometry
89+
90+
inline void fromRawValue(const RawValue &value, Point &result) {
91+
if (value.hasType<better::map<std::string, Float>>()) {
92+
auto map = (better::map<std::string, Float>)value;
93+
for (const auto &pair : map) {
94+
if (pair.first == "x") {
95+
result.x = pair.second;
96+
} else if (pair.first == "y") {
97+
result.y = pair.second;
98+
}
99+
}
100+
return;
101+
}
102+
103+
react_native_assert(value.hasType<std::vector<Float>>());
104+
if (value.hasType<std::vector<Float>>()) {
105+
auto array = (std::vector<Float>)value;
106+
react_native_assert(array.size() == 2);
107+
if (array.size() >= 2) {
108+
result = {array.at(0), array.at(1)};
109+
} else {
110+
result = {0, 0};
111+
LOG(ERROR) << "Unsupported Point vector size: " << array.size();
112+
}
113+
} else {
114+
LOG(ERROR) << "Unsupported Point type";
115+
}
116+
}
117+
118+
inline void fromRawValue(const RawValue &value, Size &result) {
119+
if (value.hasType<better::map<std::string, Float>>()) {
120+
auto map = (better::map<std::string, Float>)value;
121+
for (const auto &pair : map) {
122+
if (pair.first == "width") {
123+
result.width = pair.second;
124+
} else if (pair.first == "height") {
125+
result.height = pair.second;
126+
} else {
127+
LOG(ERROR) << "Unsupported Size map key: " << pair.first;
128+
react_native_assert(false);
129+
}
130+
}
131+
return;
132+
}
133+
134+
react_native_assert(value.hasType<std::vector<Float>>());
135+
if (value.hasType<std::vector<Float>>()) {
136+
auto array = (std::vector<Float>)value;
137+
react_native_assert(array.size() == 2);
138+
if (array.size() >= 2) {
139+
result = {array.at(0), array.at(1)};
140+
} else {
141+
result = {0, 0};
142+
LOG(ERROR) << "Unsupported Size vector size: " << array.size();
143+
}
144+
} else {
145+
LOG(ERROR) << "Unsupported Size type";
146+
}
147+
}
148+
149+
inline void fromRawValue(const RawValue &value, EdgeInsets &result) {
150+
if (value.hasType<Float>()) {
151+
auto number = (Float)value;
152+
result = {number, number, number, number};
153+
}
154+
155+
if (value.hasType<better::map<std::string, Float>>()) {
156+
auto map = (better::map<std::string, Float>)value;
157+
for (const auto &pair : map) {
158+
if (pair.first == "top") {
159+
result.top = pair.second;
160+
} else if (pair.first == "left") {
161+
result.left = pair.second;
162+
} else if (pair.first == "bottom") {
163+
result.bottom = pair.second;
164+
} else if (pair.first == "right") {
165+
result.right = pair.second;
166+
} else {
167+
LOG(ERROR) << "Unsupported EdgeInsets map key: " << pair.first;
168+
react_native_assert(false);
169+
}
170+
}
171+
return;
172+
}
173+
174+
react_native_assert(value.hasType<std::vector<Float>>());
175+
if (value.hasType<std::vector<Float>>()) {
176+
auto array = (std::vector<Float>)value;
177+
react_native_assert(array.size() == 4);
178+
if (array.size() >= 4) {
179+
result = {array.at(0), array.at(1), array.at(2), array.at(3)};
180+
} else {
181+
result = {0, 0, 0, 0};
182+
LOG(ERROR) << "Unsupported EdgeInsets vector size: " << array.size();
183+
}
184+
} else {
185+
LOG(ERROR) << "Unsupported EdgeInsets type";
186+
}
187+
}
188+
189+
inline void fromRawValue(const RawValue &value, CornerInsets &result) {
190+
if (value.hasType<Float>()) {
191+
auto number = (Float)value;
192+
result = {number, number, number, number};
193+
return;
194+
}
195+
196+
if (value.hasType<better::map<std::string, Float>>()) {
197+
auto map = (better::map<std::string, Float>)value;
198+
for (const auto &pair : map) {
199+
if (pair.first == "topLeft") {
200+
result.topLeft = pair.second;
201+
} else if (pair.first == "topRight") {
202+
result.topRight = pair.second;
203+
} else if (pair.first == "bottomLeft") {
204+
result.bottomLeft = pair.second;
205+
} else if (pair.first == "bottomRight") {
206+
result.bottomRight = pair.second;
207+
} else {
208+
LOG(ERROR) << "Unsupported CornerInsets map key: " << pair.first;
209+
react_native_assert(false);
210+
}
211+
}
212+
return;
213+
}
214+
215+
react_native_assert(value.hasType<std::vector<Float>>());
216+
if (value.hasType<std::vector<Float>>()) {
217+
auto array = (std::vector<Float>)value;
218+
react_native_assert(array.size() == 4);
219+
if (array.size() >= 4) {
220+
result = {array.at(0), array.at(1), array.at(2), array.at(3)};
221+
} else {
222+
LOG(ERROR) << "Unsupported CornerInsets vector size: " << array.size();
223+
}
224+
}
225+
226+
// Error case - we should only here if all other supported cases fail
227+
// In dev we would crash on assert before this point
228+
result = {0, 0, 0, 0};
229+
LOG(ERROR) << "Unsupported CornerInsets type";
230+
}
231+
232+
inline std::string toString(const Point &point) {
233+
return "{" + folly::to<std::string>(point.x) + ", " + folly::to<std::string>(point.y) + "}";
234+
}
235+
236+
inline std::string toString(const Size &size) {
237+
return "{" + folly::to<std::string>(size.width) + ", " + folly::to<std::string>(size.height) + "}";
238+
}
239+
240+
inline std::string toString(const Rect &rect) {
241+
return "{" + toString(rect.origin) + ", " + toString(rect.size) + "}";
242+
}
243+
244+
inline std::string toString(const EdgeInsets &edgeInsets) {
245+
return "{" + folly::to<std::string>(edgeInsets.left) + ", " + folly::to<std::string>(edgeInsets.top) + ", " +
246+
folly::to<std::string>(edgeInsets.right) + ", " + folly::to<std::string>(edgeInsets.bottom) + "}";
247+
}
248+
249+
inline std::string toString(const CornerInsets &cornerInsets) {
250+
return "{" + folly::to<std::string>(cornerInsets.topLeft) + ", " + folly::to<std::string>(cornerInsets.topRight) +
251+
", " + folly::to<std::string>(cornerInsets.bottomLeft) + ", " + folly::to<std::string>(cornerInsets.bottomRight) +
252+
"}";
253+
}
254+
255+
} // namespace react
256+
} // namespace facebook

0 commit comments

Comments
 (0)