-
Notifications
You must be signed in to change notification settings - Fork 24.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: linear gradient android #45433
feat: linear gradient android #45433
Conversation
wip wip wip parse position linear gradient start end points wip wip simplify angle to points wip gradients array wip wip wip wip wip wip wip merge main wip wip nit weird white space parser wip cleanup cleanup cleanup gradient example
Base commit: 2eb7bcb |
This is a fun surprise 😃. I chatted with a couple other engineers on RN to think about how this fits into some of the CSS stuff we have been planning. I think broadly:
A way to unblock some of this would be to follow a pattern we are using, of adding the functionality first under We are also in the middle of refactoring of background management in order to start rolling out box shadow. Part of the strategy we are taking there is to put less directly in |
Just to add to what @NickGerleman said. Even in StyleX we don't allow |
Thanks @NickGerleman @necolas I had been checking the commits on Fabric CSS Parser by @NickGerleman 🙌 and knew it will eventually gonna replace the JS processors. Also i was wondering if team was planning to leverage static hermes in future so JS processors kinda becomes auto native. Anyways, I still wanted to make the PR to get some feedback 😄 So based on your feedback I think next steps for me are.
Let me know how this sounds @NickGerleman. Thank you for your contributions and quick feedback! 🙏 |
That sounds about right to me! I didn’t think about clipping considerations. Currently we clip most often in onDraw(), so all foreground content gets clipped to the padding box. This is why background drawables can still draw outside of the clip region, but why they aren’t automatically clipped. Feel free to keep this inside CSSBackgroundDrawable for now, if it makes border use easier. The connective tissue the shadows are using is still being checked in right now. |
@NickGerleman updated the PR. Will experiment with Drawables and closely monitor the BoxShadow updates. Let me know if any more feedback. Next, will update the iOS implementation for prop name changes (it will use most of the stuff from this PR) |
export type BackgroundImagePrimitive = { | ||
type: 'linearGradient', | ||
start: {x: number, y: number}, | ||
end: {x: number, y: number}, | ||
colorStops: $ReadOnlyArray<{ | ||
color: ____ColorValue_Internal, | ||
position: number, | ||
}>, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably want to keep this in StyleSheetTypes
, since apart from the processor, this forms the allowed style input.
A nit here, is that Primitive
started propagating after "filter primitive" was used, referring to something in the filters spec unrelated to the actual input. I would love to avoid it in the future if we can.
The spec tends to refer to a single usage of this in a background to be either "background image" or "background image layer" to refer to what this is representing. And each background layer itself an <image>
, which is either a either <url>
or <gradient>
, where then a <gradient>
can be one of multiple types. But these may be used outside backgrounds.
Putting that altogether, I think this might make sense as something like a GradientValue
type, or even just LinearGradient
so we can built that hierarchy in the future, and reuse this for other places with gradients.
}; | ||
|
||
export default function processBackgroundImage( | ||
backgroundImage: $ReadOnlyArray<BackgroundImagePrimitive> | string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add handling for nullish input? Recently hit us somewhere else.
} | ||
|
||
function parseCSSLinearGradient( | ||
cssString: string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this handle case insensitivity correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. It didn't, fixed and added testcases.
start: bg.start, | ||
end: bg.end, | ||
colorStops: bg.colorStops.map(stop => { | ||
const processedColor = processColor(stop.color) ?? TRANSPARENT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it should be a parse error (return nothing) instead of becoming transparent. Could we add test for this/other error paths?
|
||
export default function processBackgroundImage( | ||
backgroundImage: $ReadOnlyArray<BackgroundImagePrimitive> | string, | ||
): $ReadOnlyArray<BackgroundImagePrimitive> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The output has some more guarantees that the input doesn't. E.g. output of processor ColorValue
should become ProcessedColorValue
, right?
void setBackgroundImage(T view, @Nullable ReadableArray backgroundImage); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we will only implement this for <View>
for now, we probably want to keep this off of BaseViewManager
.
I've been working to get us to share more of the background drawing infra, eventually moving some of these to BaseViewManager, but it will take time.
experimental_backgroundImage: { | ||
process: require('../StyleSheet/processBackgroundImage').default, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to comment below about keeping this off of BaseViewManager, if we start with just <View>
, could we keep local to that view manager and viewconfig?
@@ -0,0 +1,74 @@ | |||
package com.facebook.react.uimanager.drawable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- We're wanting all new code to be Kotlin instead of Java.
- Could we put this in
com.facebook.react.uimanager.style
instead ofcom.facebook.react.uimanager.drawable
? Potentially just named something likeGradient
orLinearGradient
?
@@ -166,6 +166,14 @@ BaseViewProps::BaseViewProps( | |||
"experimental_filter", | |||
sourceProps.filter, | |||
{})), | |||
backgroundImage( | |||
CoreFeatures::enablePropIteratorSetter ? sourceProps.backgroundImage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also add to the area around RAW_SET_PROP_SWITCH_CASE_BASIC
enablePropIteratorSetter
is still enabled in a few places internally, and if enabled, it goes through that path, in setProp
, instead of the convertRawProp
here.
@@ -62,6 +62,7 @@ void ViewShadowNode::initialize() noexcept { | |||
viewProps.importantForAccessibility != ImportantForAccessibility::Auto || | |||
viewProps.removeClippedSubviews || viewProps.cursor != Cursor::Auto || | |||
!viewProps.filter.empty() || | |||
!viewProps.backgroundImage.empty() || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the only prop guilty of this, but I think this one should go in formsView
, instead of formsStackingContext
. I.e. we don't want views with a background image to be flattened, but they also shouldn't form their own stacking context for z-ordering (at least on browsers https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context)
LinearGradient, | ||
}; | ||
|
||
struct BackgroundImagePrimitive { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Nick already mentioned it, but as a reminder for this location as well. The original "primitive" was erroneously added by myself after misinterpreting some verbiage in the spec. We have since renamed it and should probably rename it here too.
null value testcases multiple gradient new line testcase multiple gradient new line testcase
aa80d7e
to
0c4e20a
Compare
cc - @NickGerleman @joevilches addressed all the PR changes, let me know if anything else is needed. Thanks for review! |
There are some more specs like supporting px values in color stop position (right now only supports percentage) and color stop hints syntax. We can do that as quick follow ups. Wanted to keep initial implementation simple but matching to spec. |
Looking good! One final bit, that I missed initially for box shadows, is that some checks related to view configs need us to do something special when tying to viewconfig process function. I.e. to pattern match f44476d |
yeah faced the same issue. Added this check here and customType that fixed the issue. |
@NickGerleman has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
mPaint.setShader(getGradientShader()); | ||
canvas.drawRect(getBounds(), mPaint); | ||
mPaint.setShader(null); | ||
} else if (Color.alpha(useColor) != 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not important for this change, but I think we want to support background color and images at the same time.
https://www.w3.org/TR/css-backgrounds-3/#background-color
This property sets the background color of a box. This color is drawn behind any background images.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This color is drawn behind any background images.
True, letting it get drawn behind image should be good.
e.g. also tested, below works fine.
if (Color.alpha(useColor) != 0) {}
if (mGradients != null && mGradients.length > 0) {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. 2f1bf9e.
@@ -88,6 +91,22 @@ public void setTVPreferredFocus(ReactViewGroup view, boolean hasTVPreferredFocus | |||
} | |||
} | |||
|
|||
@ReactProp(name = ViewProps.BACKGROUND_IMAGE, customType = "BackgroundImage") | |||
public void setBackgroundImage(ReactViewGroup view, @Nullable ReadableArray backgroundImage) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After this lands, this change will intersect a bit with another one, where I have been refactoring to be able to eventually apply background styles as part of BaseViewManager. bbcca54
This also enables the infra for CompositeBackgroundDrawable
, for any future cases where we want to use individual Drawables as units of layering (like, if we wanted to mix Fresco provided bitmap image drawables, between gradients, or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CompositeBackgroundDrawable
would be cool!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is kind of similar to iOS view layers. Exciting!
@NickGerleman merged this pull request in bd0aedc. |
This pull request was successfully merged by @intergalacticspacehighway in bd0aedc When will my fix make it into a release? | How to file a pick request? |
Summary: - Adds `background` prop that supports CSS's linear gradient. Later this can be extended to support various other gradients and possibly CSS's background image (less motivation as better solutions exists for image) - Uses `CAGradientlayer` to draw Linear Gradient layers. So it is GPU optimised under the hood. - Style supports JS object to specify `LinearGradient`, so it can support Animated libraries. ## Changelog: [IOS] [ADDED] - linear gradient <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests Pull Request resolved: #45434 Test Plan: - Check out `processBackground-test.js` for supported syntax testcases. - Checkout example added in ViewExample.js Although the PR is tested well but open to any changes/feedback on the approach taken. Android PR - #45433. Separated the PRs to keep it easier to review. Both PRs can be reviewed individually. Reviewed By: NickGerleman Differential Revision: D60791581 Pulled By: joevilches fbshipit-source-id: 051088fdf68d9fe20c0c306f1f1c591cbd77f3d5
Summary:
backgroundImage
prop that supports CSS's linear gradient. Later this can be extended to support various other gradients and possibly CSS's background image (less motivation as better solutions exists for image)CSSBackgroundDrawable
to draw Linear Gradient shader while preserving the border style support.LinearGradient
, so it can support Animated libraries.Changelog:
[ANDROID] [ADDED] - linear gradient
Test Plan:
processBackground-test.js
for supported syntax testcases.LinearGradientExample.js
Although the PR is tested well but open to any changes/feedback on the approach taken.
iOS PR - #45434. Separated the PRs to keep it easier to review. Both PRs can be reviewed individually.