Skip to content
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

Allows to apply different border radius to RectButton component #2792

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,26 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
view.borderRadius = borderRadius
}

@ReactProp(name = "borderTopLeftRadius")
override fun setBorderTopLeftRadius(view: ButtonViewGroup, borderTopLeftRadius: Float) {
view.borderTopLeftRadius = borderTopLeftRadius
}

@ReactProp(name = "borderTopRightRadius")
override fun setBorderTopRightRadius(view: ButtonViewGroup, borderTopRightRadius: Float) {
view.borderTopRightRadius = borderTopRightRadius
}

@ReactProp(name = "borderBottomLeftRadius")
override fun setBorderBottomLeftRadius(view: ButtonViewGroup, borderBottomLeftRadius: Float) {
view.borderBottomLeftRadius = borderBottomLeftRadius
}

@ReactProp(name = "borderBottomRightRadius")
override fun setBorderBottomRightRadius(view: ButtonViewGroup, borderBottomRightRadius: Float) {
view.borderBottomRightRadius = borderBottomRightRadius
}

@ReactProp(name = "rippleColor")
override fun setRippleColor(view: ButtonViewGroup, rippleColor: Int?) {
view.rippleColor = rippleColor
Expand Down Expand Up @@ -115,6 +135,30 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
set(radius) = withBackgroundUpdate {
field = radius * resources.displayMetrics.density
}
var borderTopLeftRadius = 0f
set(radius) = withBackgroundUpdate {
field = radius * resources.displayMetrics.density
}
var borderTopRightRadius = 0f
set(radius) = withBackgroundUpdate {
field = radius * resources.displayMetrics.density
}
var borderBottomLeftRadius = 0f
set(radius) = withBackgroundUpdate {
field = radius * resources.displayMetrics.density
}
var borderBottomRightRadius = 0f
set(radius) = withBackgroundUpdate {
field = radius * resources.displayMetrics.density
}

private val hasBorderRadii: Boolean
get() = borderRadius != 0f ||
borderTopLeftRadius != 0f ||
borderTopRightRadius != 0f ||
borderBottomLeftRadius != 0f ||
borderBottomRightRadius != 0f

var exclusive = true

private var _backgroundColor = Color.TRANSPARENT
Expand All @@ -139,6 +183,22 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
needBackgroundUpdate = true
}

private fun buildBorderRadii(): FloatArray {
// duplicate radius for each corner, as setCornerRadii expects X radius and Y radius for each
return floatArrayOf(
borderTopLeftRadius,
borderTopLeftRadius,
borderTopRightRadius,
borderTopRightRadius,
borderBottomRightRadius,
borderBottomRightRadius,
borderBottomLeftRadius,
borderBottomLeftRadius,
)
.map { if (it != 0f) it else borderRadius }
.toFloatArray()
}

override fun setBackgroundColor(color: Int) = withBackgroundUpdate {
_backgroundColor = color
}
Expand Down Expand Up @@ -188,11 +248,11 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
return false
}

private fun updateBackgroundColor(backgroundColor: Int, borderRadius: Float, selectable: Drawable?) {
private fun updateBackgroundColor(backgroundColor: Int, selectable: Drawable?) {
val colorDrawable = PaintDrawable(backgroundColor)

if (borderRadius != 0f) {
colorDrawable.setCornerRadius(borderRadius)
if (hasBorderRadii) {
colorDrawable.setCornerRadii(buildBorderRadii())
}

val layerDrawable = LayerDrawable(if (selectable != null) arrayOf(colorDrawable, selectable) else arrayOf(colorDrawable))
Expand All @@ -216,30 +276,21 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R

val selectable = createSelectableDrawable()

if (borderRadius != 0f) {
// Radius-connected lines below ought to be considered
// as a temporary solution. It do not allow to set
// different radius on each corner. However, I suppose it's fairly
// fine for button-related use cases.
// Therefore it might be used as long as:
// 1. ReactViewManager is not a generic class with a possibility to handle another ViewGroup
// 2. There's no way to force native behavior of ReactViewGroup's superclass's onTouchEvent
if (selectable is RippleDrawable) {
val mask = PaintDrawable(Color.WHITE)
mask.setCornerRadius(borderRadius)
selectable.setDrawableByLayerId(android.R.id.mask, mask)
}
if (hasBorderRadii && selectable is RippleDrawable) {
val mask = PaintDrawable(Color.WHITE)
mask.setCornerRadii(buildBorderRadii())
selectable.setDrawableByLayerId(android.R.id.mask, mask)
}

if (useDrawableOnForeground && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
foreground = selectable
if (_backgroundColor != Color.TRANSPARENT) {
updateBackgroundColor(_backgroundColor, borderRadius, null)
updateBackgroundColor(_backgroundColor, null)
}
} else if (_backgroundColor == Color.TRANSPARENT && rippleColor == null) {
background = selectable
} else {
updateBackgroundColor(_backgroundColor, borderRadius, selectable)
updateBackgroundColor(_backgroundColor, selectable)
}
}

Expand Down
2 changes: 2 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import VelocityTest from './new_api/velocityTest';

import EmptyExample from './empty/EmptyExample';
import RectButtonBorders from './release_tests/rectButton';

interface Example {
name: string;
Expand Down Expand Up @@ -121,6 +122,7 @@
{ name: 'MouseButtons', component: MouseButtons },
{ name: 'ContextMenu (web only)', component: ContextMenu },
{ name: 'PointerType', component: PointerType },
{ name: 'RectButton (borders)', component: RectButtonBorders },
],
},
{
Expand Down Expand Up @@ -203,7 +205,7 @@
renderSectionHeader={({ section: { sectionTitle } }) => (
<Text style={styles.sectionTitle}>{sectionTitle}</Text>
)}
ItemSeparatorComponent={() => <View style={styles.separator} />}

Check warning on line 208 in example/src/App.tsx

View workflow job for this annotation

GitHub Actions / check (example)

Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component “MainScreen” and pass data as props. If you want to allow component creation in props, set allowAsProps option to true
/>
);
}
Expand Down
94 changes: 94 additions & 0 deletions example/src/release_tests/rectButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { RectButton } from 'react-native-gesture-handler';

export default function RectButtonBorders() {
return (
<View style={styles.container}>
<Button type="borderRadius" />
<Button type="borderTopLeftRadius" />
<Button type="borderTopRightRadius" />
<Button type="borderBottomLeftRadius" />
<Button type="borderBottomRightRadius" />
<RectButton
style={[
styles.button,
{ borderTopLeftRadius: 16, borderTopRightRadius: 16 },
]}
onPress={() => alert(`Pressed borderTopRadius!`)}>
<Text>border Top Radius</Text>
</RectButton>
<RectButton
style={[
styles.button,
{ borderBottomLeftRadius: 16, borderBottomRightRadius: 16 },
]}
onPress={() => alert(`Pressed borderTopRadius!`)}>
<Text>border Bottom Radius</Text>
</RectButton>
<RectButton
style={[
styles.button,
{
borderRadius: 8,
borderTopLeftRadius: 32,
borderTopRightRadius: 32,
},
]}
onPress={() => alert(`Pressed borderTopRadius!`)}>
<Text>borderRadius and Top Radius</Text>
</RectButton>
<RectButton
style={[
styles.button,
{
borderRadius: 8,
borderBottomLeftRadius: 32,
borderBottomRightRadius: 32,
},
]}
onPress={() => alert(`Pressed borderTopRadius!`)}>
<Text>borderRadius and Bottom Radius</Text>
</RectButton>
</View>
);
}

type BorderTypes =
| 'borderRadius'
| 'borderTopLeftRadius'
| 'borderTopRightRadius'
| 'borderBottomLeftRadius'
| 'borderBottomRightRadius';

function Button({ type }: { type: BorderTypes }) {
return (
<RectButton
style={[styles.button, { [type]: 16 }]}
onPress={() => alert(`Pressed ${type}!`)}>
<Text style={styles.text}>{type}</Text>
</RectButton>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
gap: 8,
flexWrap: 'wrap',
justifyContent: 'center',
alignContent: 'center',
},
button: {
width: 100,
height: 100,
backgroundColor: '#782aeb',
justifyContent: 'center',
alignItems: 'center',
padding: 8,
},
text: {
color: '#f8f9ff',
},
});
Loading