From 99b70981a25392e94dc0c299928f617f5c8049b6 Mon Sep 17 00:00:00 2001 From: Andrey Kalmatskiy Date: Fri, 28 Jun 2024 07:19:49 -0700 Subject: [PATCH] Add mul/div by scaler to Dimen Summary: Support multiplication/division of `Dimen` by non-dimensional scaler: ```Kotlin 1.px * 42.0 // 42.px val padding = component.gap / 2.0 ``` Reviewed By: zielinskimz Differential Revision: D59061617 fbshipit-source-id: 6c551cfce034cba58a17e8c9612965943b8d3624 --- .../java/com/facebook/rendercore/Dimen.kt | 78 +++++++++++++++++++ .../java/com/facebook/rendercore/DimenTest.kt | 40 ++++++++++ 2 files changed, 118 insertions(+) diff --git a/litho-rendercore/src/main/java/com/facebook/rendercore/Dimen.kt b/litho-rendercore/src/main/java/com/facebook/rendercore/Dimen.kt index c956d63e760..0e58bed4668 100644 --- a/litho-rendercore/src/main/java/com/facebook/rendercore/Dimen.kt +++ b/litho-rendercore/src/main/java/com/facebook/rendercore/Dimen.kt @@ -49,8 +49,86 @@ value class Dimen @PublishedApi internal constructor(val encodedValue: Long) { else -> "NaN" } } + + operator fun times(other: Double): Dimen { + return Dimen( + when { + // DP case + encodedValue and NAN_MASK != NAN_MASK -> + doubleToRawLongBits(longBitsToDouble(encodedValue) * other) + // PX case + encodedValue and PX_FLAG == PX_FLAG -> + encodePxInt(((encodedValue and PAYLOAD_MASK).toInt() * other).toInt()) + // SP case + encodedValue and SP_FLAG == SP_FLAG -> + encodeSpFloat( + (intBitsToFloat((encodedValue and PAYLOAD_MASK).toInt()) * other).toFloat()) + else -> doubleToRawLongBits(Double.NaN) + }) + } + + operator fun times(other: Float): Dimen { + return Dimen( + when { + // DP case + encodedValue and NAN_MASK != NAN_MASK -> + doubleToRawLongBits(longBitsToDouble(encodedValue) * other) + // PX case + encodedValue and PX_FLAG == PX_FLAG -> + encodePxInt(((encodedValue and PAYLOAD_MASK).toInt() * other).toInt()) + // SP case + encodedValue and SP_FLAG == SP_FLAG -> + encodeSpFloat( + (intBitsToFloat((encodedValue and PAYLOAD_MASK).toInt()) * other).toFloat()) + else -> doubleToRawLongBits(Double.NaN) + }) + } + + operator fun times(other: Int): Dimen { + return Dimen( + when { + // DP case + encodedValue and NAN_MASK != NAN_MASK -> + doubleToRawLongBits(longBitsToDouble(encodedValue) * other) + // PX case + encodedValue and PX_FLAG == PX_FLAG -> + encodePxInt((encodedValue and PAYLOAD_MASK).toInt() * other) + // SP case + encodedValue and SP_FLAG == SP_FLAG -> + encodeSpFloat(intBitsToFloat(encodedValue.and(PAYLOAD_MASK).toInt()) * other) + else -> doubleToRawLongBits(Double.NaN) + }) + } + + operator fun div(other: Double): Dimen = this * (1.0 / other) + + operator fun div(other: Float): Dimen = this * (1f / other) + + operator fun div(other: Int): Dimen { + return Dimen( + when { + // DP case + encodedValue and NAN_MASK != NAN_MASK -> + doubleToRawLongBits(longBitsToDouble(encodedValue) / other) + // PX case + encodedValue and PX_FLAG == PX_FLAG -> + encodePxInt((encodedValue and PAYLOAD_MASK).toInt() / other) + // SP case + encodedValue and SP_FLAG == SP_FLAG -> + encodeSpFloat(intBitsToFloat(encodedValue.and(PAYLOAD_MASK).toInt()) / other) + else -> doubleToRawLongBits(Double.NaN) + }) + } + + operator fun unaryMinus(): Dimen = this * -1 } +operator fun Double.times(other: Dimen): Dimen = other * this + +operator fun Float.times(other: Dimen): Dimen = other * this + +operator fun Int.times(other: Dimen): Dimen = other * this + /** Creates a Dimen with a constant dp (density-independent pixels) value. */ inline val Int.dp: Dimen get() = Dimen(doubleToRawLongBits(this.toDouble())) diff --git a/litho-rendercore/src/test/java/com/facebook/rendercore/DimenTest.kt b/litho-rendercore/src/test/java/com/facebook/rendercore/DimenTest.kt index 5806dc26040..53d4e030f2b 100644 --- a/litho-rendercore/src/test/java/com/facebook/rendercore/DimenTest.kt +++ b/litho-rendercore/src/test/java/com/facebook/rendercore/DimenTest.kt @@ -197,6 +197,46 @@ class DimenTest { Dimen(Double.NaN.toRawBits()).toPixels(resourceResolver) } + @Test + fun `dimen mul div`() { + assertThat(10.sp * 5.0).isEqualTo(50.sp) + assertThat(10.dp * 5.0).isEqualTo(50.dp) + assertThat(10.px * 5.0).isEqualTo(50.px) + assertThat(5.0 * 10.sp).isEqualTo(50.sp) + assertThat(5.0 * 10.dp).isEqualTo(50.dp) + assertThat(5.0 * 10.px).isEqualTo(50.px) + + assertThat(10.sp / 2.0).isEqualTo(5.sp) + assertThat(10.dp / 2.0).isEqualTo(5.dp) + assertThat(10.px / 2.0).isEqualTo(5.px) + + assertThat(10.sp * 5f).isEqualTo(50.sp) + assertThat(10.dp * 5f).isEqualTo(50.dp) + assertThat(10.px * 5f).isEqualTo(50.px) + assertThat(5f * 10.sp).isEqualTo(50.sp) + assertThat(5f * 10.dp).isEqualTo(50.dp) + assertThat(5f * 10.px).isEqualTo(50.px) + + assertThat(10.sp / 2f).isEqualTo(5.sp) + assertThat(10.dp / 2f).isEqualTo(5.dp) + assertThat(10.px / 2f).isEqualTo(5.px) + + assertThat(10.sp * 5).isEqualTo(50.sp) + assertThat(10.dp * 5).isEqualTo(50.dp) + assertThat(10.px * 5).isEqualTo(50.px) + assertThat(5 * 10.sp).isEqualTo(50.sp) + assertThat(5 * 10.dp).isEqualTo(50.dp) + assertThat(5 * 10.px).isEqualTo(50.px) + + assertThat(10.sp / 2).isEqualTo(5.sp) + assertThat(10.dp / 2).isEqualTo(5.dp) + assertThat(10.px / 2).isEqualTo(5.px) + + assertThat(-(10.sp)).isEqualTo((-10).sp) + assertThat(-(10.dp)).isEqualTo((-10).dp) + assertThat(-(10.px)).isEqualTo((-10).px) + } + private class MockResourceResolver(val density: Float, val scaledDensity: Float) : ResourceResolver( ApplicationProvider.getApplicationContext(),