Skip to content

Commit

Permalink
Add Fabric dynamic color support (#42117)
Browse files Browse the repository at this point in the history
Summary:
Fixes #42006 .

## Changelog:

[IOS] [ADDED] - Add Fabric dynamic color support

Pull Request resolved: #42117

Test Plan:
Dynamic color worked.

![image](https://github.com/facebook/react-native/assets/5061845/c6e988c1-9d6b-4c09-a5e9-2f1960bbfcc5)

![image](https://github.com/facebook/react-native/assets/5061845/dcccdef7-990b-4367-b21f-38cabc7040ae)

Reviewed By: javache, sammy-SC

Differential Revision: D52512672

Pulled By: cipolleschi

fbshipit-source-id: f91da08e0bdd07a987289bd82585722496bdc280
  • Loading branch information
zhongwuzw authored and facebook-github-bot committed Jan 30, 2024
1 parent d00d35e commit 4c108aa
Show file tree
Hide file tree
Showing 15 changed files with 308 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ - (void)setBackgroundColor:(UIColor *)backgroundColor
_backgroundColor = backgroundColor;
}

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
[super traitCollectionDidChange:previousTraitCollection];

if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
[self invalidateLayer];
}
}

#pragma mark - RCTComponentViewProtocol

+ (ComponentDescriptorProvider)componentDescriptorProvider
Expand Down Expand Up @@ -597,6 +606,8 @@ - (void)invalidateLayer
borderMetrics.borderWidths.left == 0 ||
colorComponentsFromColor(borderMetrics.borderColors.left).alpha == 0 || self.clipsToBounds);

CGColorRef backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:self.traitCollection].CGColor;

if (useCoreAnimationBorderRendering) {
layer.mask = nil;
if (_borderLayer) {
Expand All @@ -612,7 +623,7 @@ - (void)invalidateLayer

layer.cornerCurve = CornerCurveFromBorderCurve(borderMetrics.borderCurves.topLeft);

layer.backgroundColor = _backgroundColor.CGColor;
layer.backgroundColor = backgroundColor;
} else {
if (!_borderLayer) {
_borderLayer = [CALayer new];
Expand All @@ -635,7 +646,7 @@ - (void)invalidateLayer
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii),
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths),
borderColors,
_backgroundColor.CGColor,
backgroundColor,
self.clipsToBounds);

RCTReleaseRCTBorderColors(borderColors);
Expand Down
20 changes: 2 additions & 18 deletions packages/react-native/React/Fabric/RCTConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <react/renderer/components/view/primitives.h>
#import <react/renderer/core/LayoutPrimitives.h>
#import <react/renderer/graphics/Color.h>
#import <react/renderer/graphics/RCTPlatformColorUtils.h>
#import <react/renderer/graphics/Transform.h>

NS_ASSUME_NONNULL_BEGIN
Expand All @@ -36,24 +37,7 @@ inline std::string RCTStringFromNSString(NSString *string)

inline UIColor *_Nullable RCTUIColorFromSharedColor(const facebook::react::SharedColor &sharedColor)
{
if (!sharedColor) {
return nil;
}

if (*facebook::react::clearColor() == *sharedColor) {
return [UIColor clearColor];
}

if (*facebook::react::blackColor() == *sharedColor) {
return [UIColor blackColor];
}

if (*facebook::react::whiteColor() == *sharedColor) {
return [UIColor whiteColor];
}

auto components = facebook::react::colorComponentsFromColor(sharedColor);
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
return RCTPlatformColorFromColor(*sharedColor);
}

inline CF_RETURNS_RETAINED CGColorRef _Nullable RCTCreateCGColorRefFromSharedColor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

@file:Suppress("DEPRECATION") // We want to use RCTEventEmitter for interop purposes
package com.facebook.react.views.popupmenu

import com.facebook.react.bridge.Arguments
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ class ReactPopupMenuContainer(context: Context) : FrameLayout(context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
val view = getChildAt(0)
val popupMenu = PopupMenu(context, view)
var menu: Menu? = null
menu = popupMenu.menu
var menu = popupMenu.menu
val items = menuItems
if (items != null) {
for (i in 0 until items.size()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include <react/renderer/core/ShadowNode.h>
#include <react/renderer/core/State.h>
#include <react/renderer/core/StateData.h>
#include <react/renderer/graphics/Float.h>
#include <react/utils/ContextContainer.h>

namespace facebook::react {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#include <react/renderer/core/RawProps.h>
#include <react/renderer/core/RawPropsKey.h>
#include <react/renderer/core/graphicsConversions.h>
#include <react/renderer/graphics/Color.h>

namespace facebook::react {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ class SharedColor {

SharedColor(Color color) : color_(color) {}

Color operator*() const {
Color& operator*() {
return color_;
}

const Color& operator*() const {
return color_;
}

Expand Down Expand Up @@ -61,7 +65,7 @@ SharedColor whiteColor();

template <>
struct std::hash<facebook::react::SharedColor> {
size_t operator()(facebook::react::SharedColor color) const {
return std::hash<decltype(*color)>{}(*color);
size_t operator()(const facebook::react::SharedColor& color) const {
return std::hash<facebook::react::Color>{}(*color);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Pod::Spec.new do |s|
"\"$(PODS_ROOT)/boost\"",
"\"$(PODS_TARGET_SRCROOT)/../../../\"",
"\"$(PODS_ROOT)/RCT-Folly\"",
"\"$(PODS_ROOT)/DoubleConversion\"",
"\"$(PODS_ROOT)/fmt/include\""
]

s.name = "React-graphics"
Expand Down Expand Up @@ -60,4 +62,6 @@ Pod::Spec.new do |s|
s.dependency "RCT-Folly/Fabric", folly_version
s.dependency "React-Core/Default", version
s.dependency "React-utils"
s.dependency "DoubleConversion"
s.dependency "fmt", "9.1.0"
end
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,72 @@
#pragma once

#include <react/renderer/graphics/ColorComponents.h>
#include <react/utils/hash_combine.h>
#include <cmath>

namespace facebook::react {

using Color = int32_t;
struct DynamicColor {
int32_t lightColor = 0;
int32_t darkColor = 0;
int32_t highContrastLightColor = 0;
int32_t highContrastDarkColor = 0;
};

struct Color {
Color(int32_t color);
Color(const DynamicColor& dynamicColor);
Color(const ColorComponents& components);
Color(std::shared_ptr<void> uiColor);
int32_t getColor() const;
std::shared_ptr<void> getUIColor() const {
return uiColor_;
}
ColorComponents getColorComponents() const {
float ratio = 255;
int32_t primitiveColor = getColor();
return ColorComponents{
(float)((primitiveColor >> 16) & 0xff) / ratio,
(float)((primitiveColor >> 8) & 0xff) / ratio,
(float)((primitiveColor >> 0) & 0xff) / ratio,
(float)((primitiveColor >> 24) & 0xff) / ratio};
}
bool operator==(const Color& other) const;
bool operator!=(const Color& other) const;
operator int32_t() const {
return getColor();
}

private:
std::shared_ptr<void> uiColor_;
};

namespace HostPlatformColor {
static const facebook::react::Color UndefinedColor =
std::numeric_limits<facebook::react::Color>::max();
}

#if defined(__clang__)
#define NO_DESTROY [[clang::no_destroy]]
#else
#define NO_DESTROY
#endif

NO_DESTROY static const facebook::react::Color UndefinedColor = Color(nullptr);
} // namespace HostPlatformColor

inline Color hostPlatformColorFromComponents(ColorComponents components) {
float ratio = 255;
return ((int)round(components.alpha * ratio) & 0xff) << 24 |
((int)round(components.red * ratio) & 0xff) << 16 |
((int)round(components.green * ratio) & 0xff) << 8 |
((int)round(components.blue * ratio) & 0xff);
return Color(components);
}

inline ColorComponents colorComponentsFromHostPlatformColor(Color color) {
float ratio = 255;
return ColorComponents{
(float)((color >> 16) & 0xff) / ratio,
(float)((color >> 8) & 0xff) / ratio,
(float)((color >> 0) & 0xff) / ratio,
(float)((color >> 24) & 0xff) / ratio};
return color.getColorComponents();
}

} // namespace facebook::react

template <>
struct std::hash<facebook::react::Color> {
size_t operator()(const facebook::react::Color& color) const {
auto seed = size_t{0};
facebook::react::hash_combine(seed, color.getColor());
return seed;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import "HostPlatformColor.h"

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <react/utils/ManagedObjectWrapper.h>
#import <string>

using namespace facebook::react;

NS_ASSUME_NONNULL_BEGIN

namespace facebook::react {

namespace {
UIColor *_Nullable UIColorFromInt32(int32_t intColor)
{
CGFloat a = CGFloat((intColor >> 24) & 0xFF) / 255.0;
CGFloat r = CGFloat((intColor >> 16) & 0xFF) / 255.0;
CGFloat g = CGFloat((intColor >> 8) & 0xFF) / 255.0;
CGFloat b = CGFloat(intColor & 0xFF) / 255.0;
return [UIColor colorWithRed:r green:g blue:b alpha:a];
}

UIColor *_Nullable UIColorFromDynamicColor(const facebook::react::DynamicColor &dynamicColor)
{
int32_t light = dynamicColor.lightColor;
int32_t dark = dynamicColor.darkColor;
int32_t highContrastLight = dynamicColor.highContrastLightColor;
int32_t highContrastDark = dynamicColor.highContrastDarkColor;

UIColor *lightColor = UIColorFromInt32(light);
UIColor *darkColor = UIColorFromInt32(dark);
UIColor *highContrastLightColor = UIColorFromInt32(highContrastLight);
UIColor *highContrastDarkColor = UIColorFromInt32(highContrastDark);

if (lightColor != nil && darkColor != nil) {
UIColor *color = [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull collection) {
if (collection.userInterfaceStyle == UIUserInterfaceStyleDark) {
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastDarkColor != nil) {
return highContrastDarkColor;
} else {
return darkColor;
}
} else {
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastLightColor != nil) {
return highContrastLightColor;
} else {
return lightColor;
}
}
}];
return color;
} else {
return nil;
}

return nil;
}

int32_t ColorFromUIColor(UIColor *color)
{
float ratio = 255;
CGFloat rgba[4];
[color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]];
return ((int32_t)round((float)rgba[3] * ratio) & 0xff) << 24 | ((int)round((float)rgba[0] * ratio) & 0xff) << 16 |
((int)round((float)rgba[1] * ratio) & 0xff) << 8 | ((int)round((float)rgba[2] * ratio) & 0xff);
}

int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor)
{
UIColor *color = (UIColor *)unwrapManagedObject(uiColor);
if (color) {
UITraitCollection *currentTraitCollection = [UITraitCollection currentTraitCollection];
color = [color resolvedColorWithTraitCollection:currentTraitCollection];
return ColorFromUIColor(color);
}

return 0;
}

UIColor *_Nullable UIColorFromComponentsColor(const facebook::react::ColorComponents &components)
{
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
}
} // anonymous namespace

Color::Color(int32_t color)
{
uiColor_ = wrapManagedObject(UIColorFromInt32(color));
}

Color::Color(const DynamicColor &dynamicColor)
{
uiColor_ = wrapManagedObject(UIColorFromDynamicColor(dynamicColor));
}

Color::Color(const ColorComponents &components)
{
uiColor_ = wrapManagedObject(UIColorFromComponentsColor(components));
}

Color::Color(std::shared_ptr<void> uiColor)
{
uiColor_ = std::move(uiColor);
}

bool Color::operator==(const Color &other) const
{
return (!uiColor_ && !other.uiColor_) ||
(uiColor_ && other.uiColor_ &&
[unwrapManagedObject(getUIColor()) isEqual:unwrapManagedObject(other.getUIColor())]);
}

bool Color::operator!=(const Color &other) const
{
return !(*this == other);
}

int32_t Color::getColor() const
{
return ColorFromUIColor(uiColor_);
}
} // namespace facebook::react

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 4c108aa

Please sign in to comment.