Skip to content

Commit

Permalink
Allows to apply different border radius to RectButton component (#2792)
Browse files Browse the repository at this point in the history
## Description
This PR is intended to allow the use of different border radius values
on the RectButton component. As mentioned in comments on #477 , it
doesn’t work on Android.
I tried to follow the same implementation pattern from `borderRadius`,
but using `setCornerRadii` method when informed at least one prop from
specific corner.
Also, it is possible to merge the values from `borderRadius` or from
specific corner.

<!--
Description and motivation for this PR.

Include 'Fixes #<number>' if this is fixing some issue.
-->

## Test plan

<!--
Describe how did you test this change here.
-->
I added a component (RectButtonBorders) on example app with some
variations of values to test the changes.

---------

Co-authored-by: Jakub Piasecki <[email protected]>
  • Loading branch information
camilossantos2809 and j-piasecki authored Mar 5, 2024
1 parent 74c981f commit 6557867
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 18 deletions.
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 HoverableIcons from './new_api/hoverable_icons';
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 @@ const EXAMPLES: ExamplesSection[] = [
{ name: 'MouseButtons', component: MouseButtons },
{ name: 'ContextMenu (web only)', component: ContextMenu },
{ name: 'PointerType', component: PointerType },
{ name: 'RectButton (borders)', component: RectButtonBorders },
],
},
{
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',
},
});

0 comments on commit 6557867

Please sign in to comment.