[go: nahoru, domu]

Add IntBounds. Use it in Menus and ui-tooling.

Relnote: An IntBounds unit class has been added, representing integer pixel bounds from layout. The API of PopupPositionProvider has been updated to use it.
Bug: 159596546
Test: IntBoundsTest, PopupTest
Change-Id: I0d8d03c5535c80c6808d8f9ca7a210408890e6e7
diff --git a/ui/ui-core/api/0.1.0-dev15.txt b/ui/ui-core/api/0.1.0-dev15.txt
index bcc1c79..d2dc1a0 100644
--- a/ui/ui-core/api/0.1.0-dev15.txt
+++ b/ui/ui-core/api/0.1.0-dev15.txt
@@ -821,7 +821,7 @@
   }
 
   @androidx.compose.Immutable public interface PopupPositionProvider {
-    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntOffset parentLayoutPosition, androidx.ui.unit.IntSize parentLayoutSize, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
+    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntBounds parentLayoutBounds, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
   }
 
   public final class Ref<T> {
diff --git a/ui/ui-core/api/current.txt b/ui/ui-core/api/current.txt
index bcc1c79..d2dc1a0 100644
--- a/ui/ui-core/api/current.txt
+++ b/ui/ui-core/api/current.txt
@@ -821,7 +821,7 @@
   }
 
   @androidx.compose.Immutable public interface PopupPositionProvider {
-    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntOffset parentLayoutPosition, androidx.ui.unit.IntSize parentLayoutSize, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
+    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntBounds parentLayoutBounds, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
   }
 
   public final class Ref<T> {
diff --git a/ui/ui-core/api/public_plus_experimental_0.1.0-dev15.txt b/ui/ui-core/api/public_plus_experimental_0.1.0-dev15.txt
index bcc1c79..d2dc1a0 100644
--- a/ui/ui-core/api/public_plus_experimental_0.1.0-dev15.txt
+++ b/ui/ui-core/api/public_plus_experimental_0.1.0-dev15.txt
@@ -821,7 +821,7 @@
   }
 
   @androidx.compose.Immutable public interface PopupPositionProvider {
-    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntOffset parentLayoutPosition, androidx.ui.unit.IntSize parentLayoutSize, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
+    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntBounds parentLayoutBounds, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
   }
 
   public final class Ref<T> {
diff --git a/ui/ui-core/api/public_plus_experimental_current.txt b/ui/ui-core/api/public_plus_experimental_current.txt
index bcc1c79..d2dc1a0 100644
--- a/ui/ui-core/api/public_plus_experimental_current.txt
+++ b/ui/ui-core/api/public_plus_experimental_current.txt
@@ -821,7 +821,7 @@
   }
 
   @androidx.compose.Immutable public interface PopupPositionProvider {
-    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntOffset parentLayoutPosition, androidx.ui.unit.IntSize parentLayoutSize, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
+    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntBounds parentLayoutBounds, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
   }
 
   public final class Ref<T> {
diff --git a/ui/ui-core/api/restricted_0.1.0-dev15.txt b/ui/ui-core/api/restricted_0.1.0-dev15.txt
index c24b2fe..03b9e1c 100644
--- a/ui/ui-core/api/restricted_0.1.0-dev15.txt
+++ b/ui/ui-core/api/restricted_0.1.0-dev15.txt
@@ -873,7 +873,7 @@
   }
 
   @androidx.compose.Immutable public interface PopupPositionProvider {
-    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntOffset parentLayoutPosition, androidx.ui.unit.IntSize parentLayoutSize, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
+    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntBounds parentLayoutBounds, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
   }
 
   public final class Ref<T> {
diff --git a/ui/ui-core/api/restricted_current.txt b/ui/ui-core/api/restricted_current.txt
index c24b2fe..03b9e1c 100644
--- a/ui/ui-core/api/restricted_current.txt
+++ b/ui/ui-core/api/restricted_current.txt
@@ -873,7 +873,7 @@
   }
 
   @androidx.compose.Immutable public interface PopupPositionProvider {
-    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntOffset parentLayoutPosition, androidx.ui.unit.IntSize parentLayoutSize, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
+    method public androidx.ui.unit.IntOffset calculatePosition(androidx.ui.unit.IntBounds parentLayoutBounds, androidx.ui.core.LayoutDirection layoutDirection, androidx.ui.unit.IntSize popupSize);
   }
 
   public final class Ref<T> {
diff --git a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/PopupTest.kt b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/PopupTest.kt
index 1e4c039..a6dac2c 100644
--- a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/PopupTest.kt
+++ b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/PopupTest.kt
@@ -33,9 +33,12 @@
 import androidx.ui.layout.rtl
 import androidx.ui.test.createComposeRule
 import androidx.ui.test.runOnIdleCompose
+import androidx.ui.unit.IntBounds
 import androidx.ui.unit.IntOffset
 import androidx.ui.unit.IntSize
 import androidx.ui.unit.dp
+import androidx.ui.unit.height
+import androidx.ui.unit.width
 import com.google.common.truth.Truth
 import org.hamcrest.CoreMatchers.instanceOf
 import org.hamcrest.Description
@@ -56,9 +59,9 @@
     val composeTestRule = createComposeRule(disableTransitions = true)
     private val testTag = "testedPopup"
 
-    private val parentGlobalPosition = IntOffset(50, 50)
+    private val parentBounds = IntBounds(50, 50, 150, 150)
     private val offset = IntOffset(10, 10)
-    private val parentSize = IntSize(100, 100)
+    private val parentSize = IntSize(parentBounds.width, parentBounds.height)
     private val popupSize = IntSize(40, 20)
 
     private var composeViewAbsolutePosition = IntOffset(0, 0)
@@ -492,8 +495,7 @@
 
         val positionTopStart =
             AlignmentOffsetPositionProvider(Alignment.TopStart, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -511,8 +513,7 @@
 
         val positionTopStart =
             AlignmentOffsetPositionProvider(Alignment.TopStart, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -530,8 +531,7 @@
 
         val positionTopCenter =
             AlignmentOffsetPositionProvider(Alignment.TopCenter, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -549,8 +549,7 @@
 
         val positionTopCenter =
             AlignmentOffsetPositionProvider(Alignment.TopCenter, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -568,8 +567,7 @@
 
         val positionTopEnd =
             AlignmentOffsetPositionProvider(Alignment.TopEnd, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -587,8 +585,7 @@
 
         val positionTopEnd =
             AlignmentOffsetPositionProvider(Alignment.TopEnd, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -606,8 +603,7 @@
 
         val positionCenterEnd =
             AlignmentOffsetPositionProvider(Alignment.CenterEnd, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -625,8 +621,7 @@
 
         val positionCenterEnd =
             AlignmentOffsetPositionProvider(Alignment.CenterEnd, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -644,8 +639,7 @@
 
         val positionBottomEnd =
             AlignmentOffsetPositionProvider(Alignment.BottomEnd, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -663,8 +657,7 @@
 
         val positionBottomEnd =
             AlignmentOffsetPositionProvider(Alignment.BottomEnd, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -682,8 +675,7 @@
 
         val positionBottomCenter =
             AlignmentOffsetPositionProvider(Alignment.BottomCenter, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -701,8 +693,7 @@
 
         val positionBottomCenter =
             AlignmentOffsetPositionProvider(Alignment.BottomCenter, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -720,8 +711,7 @@
 
         val positionBottomStart =
             AlignmentOffsetPositionProvider(Alignment.BottomStart, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -739,8 +729,7 @@
 
         val positionBottomStart =
             AlignmentOffsetPositionProvider(Alignment.BottomStart, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -758,8 +747,7 @@
 
         val positionCenterStart =
             AlignmentOffsetPositionProvider(Alignment.CenterStart, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -777,8 +765,7 @@
 
         val positionCenterStart =
             AlignmentOffsetPositionProvider(Alignment.CenterStart, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -796,8 +783,7 @@
 
         val positionCenter =
             AlignmentOffsetPositionProvider(Alignment.Center, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -815,8 +801,7 @@
 
         val positionCenter =
             AlignmentOffsetPositionProvider(Alignment.Center, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -855,8 +840,7 @@
 
         val positionLeft =
             DropdownPositionProvider(DropDownAlignment.Start, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -874,8 +858,7 @@
 
         val positionLeft =
             DropdownPositionProvider(DropDownAlignment.Start, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
@@ -893,8 +876,7 @@
 
         val positionRight =
             DropdownPositionProvider(DropDownAlignment.End, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Ltr,
                 popupSize
             )
@@ -912,8 +894,7 @@
 
         val positionRight =
             DropdownPositionProvider(DropDownAlignment.End, offset).calculatePosition(
-                parentGlobalPosition,
-                parentSize,
+                parentBounds,
                 LayoutDirection.Rtl,
                 popupSize
             )
diff --git a/ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidPopup.kt b/ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidPopup.kt
index c382ef3..1dd7771 100644
--- a/ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidPopup.kt
+++ b/ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidPopup.kt
@@ -38,6 +38,7 @@
 import androidx.ui.core.semantics.semantics
 import androidx.ui.geometry.Offset
 import androidx.ui.semantics.popup
+import androidx.ui.unit.IntBounds
 import androidx.ui.unit.round
 import org.jetbrains.annotations.TestOnly
 
@@ -88,8 +89,7 @@
         val layoutPosition = coordinates.localToGlobal(Offset.Zero).round()
         val layoutSize = coordinates.size
 
-        popupLayout.popupPositionProperties.parentPosition = layoutPosition
-        popupLayout.popupPositionProperties.parentSize = layoutSize
+        popupLayout.popupPositionProperties.parentBounds = IntBounds(layoutPosition, layoutSize)
 
         // Update the popup's position
         popupLayout.updatePosition()
@@ -190,8 +190,7 @@
      */
     fun updatePosition() {
         val popupGlobalPosition = popupPositionProvider.calculatePosition(
-            popupPositionProperties.parentPosition,
-            popupPositionProperties.parentSize,
+            popupPositionProperties.parentBounds,
             popupPositionProperties.parentLayoutDirection,
             popupPositionProperties.childrenSize
         )
diff --git a/ui/ui-core/src/commonMain/kotlin/androidx/ui/core/Popup.kt b/ui/ui-core/src/commonMain/kotlin/androidx/ui/core/Popup.kt
index ced7376..62567f4 100644
--- a/ui/ui-core/src/commonMain/kotlin/androidx/ui/core/Popup.kt
+++ b/ui/ui-core/src/commonMain/kotlin/androidx/ui/core/Popup.kt
@@ -21,8 +21,11 @@
 import androidx.compose.Providers
 import androidx.compose.ambientOf
 import androidx.compose.remember
+import androidx.ui.unit.IntBounds
 import androidx.ui.unit.IntOffset
 import androidx.ui.unit.IntSize
+import androidx.ui.unit.height
+import androidx.ui.unit.width
 
 /**
  * Opens a popup with the given content.
@@ -107,8 +110,7 @@
 }
 
 internal class PopupPositionProperties {
-    var parentPosition = IntOffset.Origin
-    var parentSize = IntSize.Zero
+    var parentBounds = IntBounds(0, 0, 0, 0)
     var childrenSize = IntSize.Zero
     var parentLayoutDirection: LayoutDirection = LayoutDirection.Ltr
 }
@@ -137,14 +139,12 @@
     /**
      * Calculates the position of a [Popup] on screen.
      *
-     * @param parentLayoutPosition The position of the parent wrapper layout on screen.
-     * @param parentLayoutSize The size of the parent wrapper layout.
+     * @param parentLayoutBounds The bounds of the parent wrapper layout on screen.
      * @param layoutDirection The layout direction of the parent wrapper layout.
      * @param popupSize The size of the popup to be positioned.
      */
     fun calculatePosition(
-        parentLayoutPosition: IntOffset,
-        parentLayoutSize: IntSize,
+        parentLayoutBounds: IntBounds,
         layoutDirection: LayoutDirection,
         popupSize: IntSize
     ): IntOffset
@@ -164,8 +164,7 @@
     val offset: IntOffset
 ) : PopupPositionProvider {
     override fun calculatePosition(
-        parentLayoutPosition: IntOffset,
-        parentLayoutSize: IntSize,
+        parentLayoutBounds: IntBounds,
         layoutDirection: LayoutDirection,
         popupSize: IntSize
     ): IntOffset {
@@ -174,7 +173,7 @@
 
         // Get the aligned point inside the parent
         val parentAlignmentPoint = alignment.align(
-            IntSize(parentLayoutSize.width, parentLayoutSize.height),
+            IntSize(parentLayoutBounds.width, parentLayoutBounds.height),
             layoutDirection
         )
         // Get the aligned point inside the child
@@ -184,7 +183,7 @@
         )
 
         // Add the global position of the parent
-        popupGlobalPosition += IntOffset(parentLayoutPosition.x, parentLayoutPosition.y)
+        popupGlobalPosition += IntOffset(parentLayoutBounds.left, parentLayoutBounds.top)
 
         // Add the distance between the parent's top left corner and the alignment point
         popupGlobalPosition += parentAlignmentPoint
@@ -208,39 +207,38 @@
     val offset: IntOffset
 ) : PopupPositionProvider {
     override fun calculatePosition(
-        parentLayoutPosition: IntOffset,
-        parentLayoutSize: IntSize,
+        parentLayoutBounds: IntBounds,
         layoutDirection: LayoutDirection,
         popupSize: IntSize
     ): IntOffset {
         var popupGlobalPosition = IntOffset(0, 0)
 
         // Add the global position of the parent
-        popupGlobalPosition += IntOffset(parentLayoutPosition.x, parentLayoutPosition.y)
+        popupGlobalPosition += IntOffset(parentLayoutBounds.left, parentLayoutBounds.top)
 
         /*
-        * In LTR context aligns popup's left edge with the parent's left edge for Start alignment and
-        * parent's right edge for End alignment.
-        * In RTL context aligns popup's right edge with the parent's right edge for Start alignment and
-        * parent's left edge for End alignment.
+        * In LTR context aligns popup's left edge with the parent's left edge for Start alignment
+        * and parent's right edge for End alignment.
+        * In RTL context aligns popup's right edge with the parent's right edge for Start alignment
+        * and parent's left edge for End alignment.
         */
         val alignmentPositionX =
             if (dropDownAlignment == DropDownAlignment.Start) {
                 if (layoutDirection == LayoutDirection.Ltr) {
                     0
                 } else {
-                    parentLayoutSize.width - popupSize.width
+                    parentLayoutBounds.width - popupSize.width
                 }
             } else {
                 if (layoutDirection == LayoutDirection.Ltr) {
-                    parentLayoutSize.width
+                    parentLayoutBounds.width
                 } else {
                     -popupSize.width
                 }
             }
 
         // The popup's position relative to the parent's top left corner
-        val dropdownAlignmentPosition = IntOffset(alignmentPositionX, parentLayoutSize.height)
+        val dropdownAlignmentPosition = IntOffset(alignmentPositionX, parentLayoutBounds.height)
 
         popupGlobalPosition += dropdownAlignmentPosition
 
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/MenuTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/MenuTest.kt
index 6d8bcbb..ffbe7fd 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/MenuTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/MenuTest.kt
@@ -43,13 +43,11 @@
 import androidx.ui.test.runOnIdleCompose
 import androidx.ui.test.waitForIdle
 import androidx.ui.unit.Density
+import androidx.ui.unit.IntBounds
 import androidx.ui.unit.IntOffset
 import androidx.ui.unit.IntSize
 import androidx.ui.unit.Position
-import androidx.ui.unit.PxBounds
 import androidx.ui.unit.dp
-import androidx.ui.unit.toOffset
-import androidx.ui.unit.toSize
 import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
@@ -150,8 +148,7 @@
             density,
             displayMetrics
         ).calculatePosition(
-            anchorPosition,
-            anchorSize,
+            IntBounds(anchorPosition, anchorSize),
             LayoutDirection.Ltr,
             popupSize
         )
@@ -168,8 +165,7 @@
             density,
             displayMetrics
         ).calculatePosition(
-            anchorPosition,
-            anchorSize,
+            IntBounds(anchorPosition, anchorSize),
             LayoutDirection.Rtl,
             popupSize
         )
@@ -204,8 +200,7 @@
             density,
             displayMetrics
         ).calculatePosition(
-            anchorPosition,
-            anchorSize,
+            IntBounds(anchorPosition, anchorSize),
             LayoutDirection.Ltr,
             popupSize
         )
@@ -222,8 +217,7 @@
             density,
             displayMetrics
         ).calculatePosition(
-            anchorPositionRtl,
-            anchorSize,
+            IntBounds(anchorPositionRtl, anchorSize),
             LayoutDirection.Rtl,
             popupSize
         )
@@ -252,8 +246,8 @@
         val offsetY = 40
         val popupSize = IntSize(50, 80)
 
-        var obtainedParentBounds = PxBounds(0f, 0f, 0f, 0f)
-        var obtainedMenuBounds = PxBounds(0f, 0f, 0f, 0f)
+        var obtainedParentBounds = IntBounds(0, 0, 0, 0)
+        var obtainedMenuBounds = IntBounds(0, 0, 0, 0)
         DropdownMenuPositionProvider(
             Position(offsetX.dp, offsetY.dp),
             density,
@@ -262,21 +256,18 @@
             obtainedParentBounds = parentBounds
             obtainedMenuBounds = menuBounds
         }.calculatePosition(
-            anchorPosition,
-            anchorSize,
+            IntBounds(anchorPosition, anchorSize),
             LayoutDirection.Ltr,
             popupSize
         )
 
-        assertThat(obtainedParentBounds).isEqualTo(
-            PxBounds(anchorPosition.toOffset(), anchorSize.toSize())
-        )
+        assertThat(obtainedParentBounds).isEqualTo(IntBounds(anchorPosition, anchorSize))
         assertThat(obtainedMenuBounds).isEqualTo(
-            PxBounds(
-                anchorPosition.x.toFloat() + anchorSize.width - inset + offsetX,
-                anchorPosition.y.toFloat() + anchorSize.height - inset + offsetY,
-                anchorPosition.x.toFloat() + anchorSize.width - inset + offsetX + popupSize.width,
-                anchorPosition.y.toFloat() + anchorSize.height - inset + offsetY + popupSize.height
+            IntBounds(
+                anchorPosition.x + anchorSize.width - inset + offsetX,
+                anchorPosition.y + anchorSize.height - inset + offsetY,
+                anchorPosition.x + anchorSize.width - inset + offsetX + popupSize.width,
+                anchorPosition.y + anchorSize.height - inset + offsetY + popupSize.height
             )
         )
     }
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Menu.kt b/ui/ui-material/src/main/java/androidx/ui/material/Menu.kt
index e0690d0..4942af5 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Menu.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Menu.kt
@@ -51,13 +51,11 @@
 import androidx.ui.layout.preferredWidth
 import androidx.ui.material.ripple.RippleIndication
 import androidx.ui.unit.Density
+import androidx.ui.unit.IntBounds
 import androidx.ui.unit.IntOffset
 import androidx.ui.unit.IntSize
-import androidx.ui.unit.PxBounds
 import androidx.ui.unit.dp
 import androidx.ui.unit.height
-import androidx.ui.unit.toOffset
-import androidx.ui.unit.toSize
 import androidx.ui.unit.width
 import kotlin.math.max
 import kotlin.math.min
@@ -264,20 +262,20 @@
 }
 
 private fun calculateTransformOrigin(
-    parentBounds: PxBounds,
-    menuBounds: PxBounds,
+    parentBounds: IntBounds,
+    menuBounds: IntBounds,
     density: Density
 ): TransformOrigin {
-    val inset = with(density) { MenuElevationInset.toPx() }
-    val realMenuBounds = PxBounds(
+    val inset = with(density) { MenuElevationInset.toIntPx() }
+    val realMenuBounds = IntBounds(
         menuBounds.left + inset,
         menuBounds.top + inset,
         menuBounds.right - inset,
         menuBounds.bottom - inset
     )
     val pivotX = when {
-        realMenuBounds.left >= parentBounds.right -> 0f
-        realMenuBounds.right <= parentBounds.left -> 1f
+        realMenuBounds.left >= parentBounds.right -> 0
+        realMenuBounds.right <= parentBounds.left -> 1
         else -> {
             val intersectionCenter =
                 (max(parentBounds.left, realMenuBounds.left) +
@@ -286,8 +284,8 @@
         }
     }
     val pivotY = when {
-        realMenuBounds.top >= parentBounds.bottom -> 0f
-        realMenuBounds.bottom <= parentBounds.top -> 1f
+        realMenuBounds.top >= parentBounds.bottom -> 0
+        realMenuBounds.bottom <= parentBounds.top -> 1
         else -> {
             val intersectionCenter =
                 (max(parentBounds.top, realMenuBounds.top) +
@@ -295,7 +293,7 @@
             (intersectionCenter + inset - menuBounds.top) / menuBounds.height
         }
     }
-    return TransformOrigin(pivotX, pivotY)
+    return TransformOrigin(pivotX.toFloat(), pivotY.toFloat())
 }
 
 // Menu positioning.
@@ -309,11 +307,10 @@
     val contentOffset: Position,
     val density: Density,
     val displayMetrics: DisplayMetrics,
-    val onPositionCalculated: (PxBounds, PxBounds) -> Unit = { _, _ -> }
+    val onPositionCalculated: (IntBounds, IntBounds) -> Unit = { _, _ -> }
 ) : PopupPositionProvider {
     override fun calculatePosition(
-        parentLayoutPosition: IntOffset,
-        parentLayoutSize: IntSize,
+        parentLayoutBounds: IntBounds,
         layoutDirection: LayoutDirection,
         popupSize: IntSize
     ): IntOffset {
@@ -325,12 +322,10 @@
         val realPopupHeight = popupSize.height - inset * 2
         val contentOffsetX = with(density) { contentOffset.x.toIntPx() }
         val contentOffsetY = with(density) { contentOffset.y.toIntPx() }
-        val parentRight = parentLayoutPosition.x + parentLayoutSize.width
-        val parentBottom = parentLayoutPosition.y + parentLayoutSize.height
 
         // Compute horizontal position.
-        val toRight = parentRight + contentOffsetX
-        val toLeft = parentLayoutPosition.x - contentOffsetX - realPopupWidth
+        val toRight = parentLayoutBounds.right + contentOffsetX
+        val toLeft = parentLayoutBounds.left - contentOffsetX - realPopupWidth
         val toDisplayRight = displayMetrics.widthPixels - realPopupWidth
         val toDisplayLeft = 0
         val x = if (layoutDirection == LayoutDirection.Ltr) {
@@ -342,24 +337,18 @@
         } ?: toLeft
 
         // Compute vertical position.
-        val toBottom = parentBottom + contentOffsetY
-        val toTop = parentLayoutPosition.y - contentOffsetY - realPopupHeight
-        val toCenter = parentLayoutPosition.y - realPopupHeight / 2
+        val toBottom = parentLayoutBounds.bottom + contentOffsetY
+        val toTop = parentLayoutBounds.top - contentOffsetY - realPopupHeight
+        val toCenter = parentLayoutBounds.top - realPopupHeight / 2
         val toDisplayBottom = displayMetrics.heightPixels - realPopupHeight - verticalMargin
         val y = sequenceOf(toBottom, toTop, toCenter, toDisplayBottom).firstOrNull {
             it >= verticalMargin &&
                     it + realPopupHeight <= displayMetrics.heightPixels - verticalMargin
         } ?: toTop
 
-        // TODO(popam, b/159596546): we should probably have androidx.ui.unit.IntBounds instead
         onPositionCalculated(
-            PxBounds(parentLayoutPosition.toOffset(), parentLayoutSize.toSize()),
-            PxBounds(
-                x.toFloat() - inset,
-                y.toFloat() - inset,
-                x.toFloat() + inset + realPopupWidth,
-                y.toFloat() + inset + realPopupHeight
-            )
+            parentLayoutBounds,
+            IntBounds(x - inset, y - inset, x + inset + realPopupWidth, y + inset + realPopupHeight)
         )
         return IntOffset(x - inset, y - inset)
     }
diff --git a/ui/ui-tooling/api/0.1.0-dev15.txt b/ui/ui-tooling/api/0.1.0-dev15.txt
index 822f2c9..478773e 100644
--- a/ui/ui-tooling/api/0.1.0-dev15.txt
+++ b/ui/ui-tooling/api/0.1.0-dev15.txt
@@ -1,25 +1,12 @@
 // Signature format: 3.0
 package androidx.ui.tooling {
 
-  public final class Bounds {
-    ctor public Bounds(int left, int top, int right, int bottom);
-    method public int component1();
-    method public int component2();
-    method public int component3();
-    method public int component4();
-    method public androidx.ui.tooling.Bounds copy(int left, int top, int right, int bottom);
-    method public int getBottom();
-    method public int getLeft();
-    method public int getRight();
-    method public int getTop();
-  }
-
   public final class CallGroup extends androidx.ui.tooling.Group {
-    ctor public CallGroup(Object? key, androidx.ui.tooling.Bounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public CallGroup(Object? key, androidx.ui.unit.IntBounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
   }
 
   public abstract sealed class Group {
-    method public final androidx.ui.tooling.Bounds getBox();
+    method public final androidx.ui.unit.IntBounds getBox();
     method public final java.util.Collection<androidx.ui.tooling.Group> getChildren();
     method public final java.util.Collection<java.lang.Object> getData();
     method public final Object? getKey();
@@ -43,7 +30,7 @@
   }
 
   public final class NodeGroup extends androidx.ui.tooling.Group {
-    ctor public NodeGroup(Object? key, Object node, androidx.ui.tooling.Bounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public NodeGroup(Object? key, Object node, androidx.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
     method public Object getNode();
   }
 
diff --git a/ui/ui-tooling/api/current.txt b/ui/ui-tooling/api/current.txt
index 822f2c9..478773e 100644
--- a/ui/ui-tooling/api/current.txt
+++ b/ui/ui-tooling/api/current.txt
@@ -1,25 +1,12 @@
 // Signature format: 3.0
 package androidx.ui.tooling {
 
-  public final class Bounds {
-    ctor public Bounds(int left, int top, int right, int bottom);
-    method public int component1();
-    method public int component2();
-    method public int component3();
-    method public int component4();
-    method public androidx.ui.tooling.Bounds copy(int left, int top, int right, int bottom);
-    method public int getBottom();
-    method public int getLeft();
-    method public int getRight();
-    method public int getTop();
-  }
-
   public final class CallGroup extends androidx.ui.tooling.Group {
-    ctor public CallGroup(Object? key, androidx.ui.tooling.Bounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public CallGroup(Object? key, androidx.ui.unit.IntBounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
   }
 
   public abstract sealed class Group {
-    method public final androidx.ui.tooling.Bounds getBox();
+    method public final androidx.ui.unit.IntBounds getBox();
     method public final java.util.Collection<androidx.ui.tooling.Group> getChildren();
     method public final java.util.Collection<java.lang.Object> getData();
     method public final Object? getKey();
@@ -43,7 +30,7 @@
   }
 
   public final class NodeGroup extends androidx.ui.tooling.Group {
-    ctor public NodeGroup(Object? key, Object node, androidx.ui.tooling.Bounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public NodeGroup(Object? key, Object node, androidx.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
     method public Object getNode();
   }
 
diff --git a/ui/ui-tooling/api/public_plus_experimental_0.1.0-dev15.txt b/ui/ui-tooling/api/public_plus_experimental_0.1.0-dev15.txt
index 822f2c9..478773e 100644
--- a/ui/ui-tooling/api/public_plus_experimental_0.1.0-dev15.txt
+++ b/ui/ui-tooling/api/public_plus_experimental_0.1.0-dev15.txt
@@ -1,25 +1,12 @@
 // Signature format: 3.0
 package androidx.ui.tooling {
 
-  public final class Bounds {
-    ctor public Bounds(int left, int top, int right, int bottom);
-    method public int component1();
-    method public int component2();
-    method public int component3();
-    method public int component4();
-    method public androidx.ui.tooling.Bounds copy(int left, int top, int right, int bottom);
-    method public int getBottom();
-    method public int getLeft();
-    method public int getRight();
-    method public int getTop();
-  }
-
   public final class CallGroup extends androidx.ui.tooling.Group {
-    ctor public CallGroup(Object? key, androidx.ui.tooling.Bounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public CallGroup(Object? key, androidx.ui.unit.IntBounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
   }
 
   public abstract sealed class Group {
-    method public final androidx.ui.tooling.Bounds getBox();
+    method public final androidx.ui.unit.IntBounds getBox();
     method public final java.util.Collection<androidx.ui.tooling.Group> getChildren();
     method public final java.util.Collection<java.lang.Object> getData();
     method public final Object? getKey();
@@ -43,7 +30,7 @@
   }
 
   public final class NodeGroup extends androidx.ui.tooling.Group {
-    ctor public NodeGroup(Object? key, Object node, androidx.ui.tooling.Bounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public NodeGroup(Object? key, Object node, androidx.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
     method public Object getNode();
   }
 
diff --git a/ui/ui-tooling/api/public_plus_experimental_current.txt b/ui/ui-tooling/api/public_plus_experimental_current.txt
index 822f2c9..478773e 100644
--- a/ui/ui-tooling/api/public_plus_experimental_current.txt
+++ b/ui/ui-tooling/api/public_plus_experimental_current.txt
@@ -1,25 +1,12 @@
 // Signature format: 3.0
 package androidx.ui.tooling {
 
-  public final class Bounds {
-    ctor public Bounds(int left, int top, int right, int bottom);
-    method public int component1();
-    method public int component2();
-    method public int component3();
-    method public int component4();
-    method public androidx.ui.tooling.Bounds copy(int left, int top, int right, int bottom);
-    method public int getBottom();
-    method public int getLeft();
-    method public int getRight();
-    method public int getTop();
-  }
-
   public final class CallGroup extends androidx.ui.tooling.Group {
-    ctor public CallGroup(Object? key, androidx.ui.tooling.Bounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public CallGroup(Object? key, androidx.ui.unit.IntBounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
   }
 
   public abstract sealed class Group {
-    method public final androidx.ui.tooling.Bounds getBox();
+    method public final androidx.ui.unit.IntBounds getBox();
     method public final java.util.Collection<androidx.ui.tooling.Group> getChildren();
     method public final java.util.Collection<java.lang.Object> getData();
     method public final Object? getKey();
@@ -43,7 +30,7 @@
   }
 
   public final class NodeGroup extends androidx.ui.tooling.Group {
-    ctor public NodeGroup(Object? key, Object node, androidx.ui.tooling.Bounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public NodeGroup(Object? key, Object node, androidx.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
     method public Object getNode();
   }
 
diff --git a/ui/ui-tooling/api/restricted_0.1.0-dev15.txt b/ui/ui-tooling/api/restricted_0.1.0-dev15.txt
index 822f2c9..478773e 100644
--- a/ui/ui-tooling/api/restricted_0.1.0-dev15.txt
+++ b/ui/ui-tooling/api/restricted_0.1.0-dev15.txt
@@ -1,25 +1,12 @@
 // Signature format: 3.0
 package androidx.ui.tooling {
 
-  public final class Bounds {
-    ctor public Bounds(int left, int top, int right, int bottom);
-    method public int component1();
-    method public int component2();
-    method public int component3();
-    method public int component4();
-    method public androidx.ui.tooling.Bounds copy(int left, int top, int right, int bottom);
-    method public int getBottom();
-    method public int getLeft();
-    method public int getRight();
-    method public int getTop();
-  }
-
   public final class CallGroup extends androidx.ui.tooling.Group {
-    ctor public CallGroup(Object? key, androidx.ui.tooling.Bounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public CallGroup(Object? key, androidx.ui.unit.IntBounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
   }
 
   public abstract sealed class Group {
-    method public final androidx.ui.tooling.Bounds getBox();
+    method public final androidx.ui.unit.IntBounds getBox();
     method public final java.util.Collection<androidx.ui.tooling.Group> getChildren();
     method public final java.util.Collection<java.lang.Object> getData();
     method public final Object? getKey();
@@ -43,7 +30,7 @@
   }
 
   public final class NodeGroup extends androidx.ui.tooling.Group {
-    ctor public NodeGroup(Object? key, Object node, androidx.ui.tooling.Bounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public NodeGroup(Object? key, Object node, androidx.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
     method public Object getNode();
   }
 
diff --git a/ui/ui-tooling/api/restricted_current.txt b/ui/ui-tooling/api/restricted_current.txt
index 822f2c9..478773e 100644
--- a/ui/ui-tooling/api/restricted_current.txt
+++ b/ui/ui-tooling/api/restricted_current.txt
@@ -1,25 +1,12 @@
 // Signature format: 3.0
 package androidx.ui.tooling {
 
-  public final class Bounds {
-    ctor public Bounds(int left, int top, int right, int bottom);
-    method public int component1();
-    method public int component2();
-    method public int component3();
-    method public int component4();
-    method public androidx.ui.tooling.Bounds copy(int left, int top, int right, int bottom);
-    method public int getBottom();
-    method public int getLeft();
-    method public int getRight();
-    method public int getTop();
-  }
-
   public final class CallGroup extends androidx.ui.tooling.Group {
-    ctor public CallGroup(Object? key, androidx.ui.tooling.Bounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public CallGroup(Object? key, androidx.ui.unit.IntBounds box, java.util.List<androidx.ui.tooling.ParameterInformation> parameters, java.util.Collection<?> data, java.util.Collection<? extends androidx.ui.tooling.Group> children);
   }
 
   public abstract sealed class Group {
-    method public final androidx.ui.tooling.Bounds getBox();
+    method public final androidx.ui.unit.IntBounds getBox();
     method public final java.util.Collection<androidx.ui.tooling.Group> getChildren();
     method public final java.util.Collection<java.lang.Object> getData();
     method public final Object? getKey();
@@ -43,7 +30,7 @@
   }
 
   public final class NodeGroup extends androidx.ui.tooling.Group {
-    ctor public NodeGroup(Object? key, Object node, androidx.ui.tooling.Bounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
+    ctor public NodeGroup(Object? key, Object node, androidx.ui.unit.IntBounds box, java.util.Collection<?> data, java.util.List<androidx.ui.core.ModifierInfo> modifierInfo, java.util.Collection<? extends androidx.ui.tooling.Group> children);
     method public Object getNode();
   }
 
diff --git a/ui/ui-tooling/src/main/java/androidx/ui/tooling/SlotTree.kt b/ui/ui-tooling/src/main/java/androidx/ui/tooling/SlotTree.kt
index 2787f2b..e22237a 100644
--- a/ui/ui-tooling/src/main/java/androidx/ui/tooling/SlotTree.kt
+++ b/ui/ui-tooling/src/main/java/androidx/ui/tooling/SlotTree.kt
@@ -28,18 +28,12 @@
 import androidx.ui.core.LayoutNode
 import androidx.ui.core.ModifierInfo
 import androidx.ui.core.globalPosition
+import androidx.ui.unit.IntBounds
 import java.lang.reflect.Field
 import kotlin.math.max
 import kotlin.math.min
 import kotlin.math.roundToInt
 
-data class Bounds(
-    val left: Int,
-    val top: Int,
-    val right: Int,
-    val bottom: Int
-)
-
 /**
  * A group in the slot table. Represents either a call or an emitted node.
  */
@@ -52,7 +46,7 @@
     /**
      * The bounding layout box for the group.
      */
-    val box: Bounds,
+    val box: IntBounds,
 
     /**
      * Any data that was stored in the slot table for the group
@@ -88,7 +82,7 @@
  */
 class CallGroup(
     key: Any?,
-    box: Bounds,
+    box: IntBounds,
     override val parameters: List<ParameterInformation>,
     data: Collection<Any?>,
     children: Collection<Group>
@@ -104,7 +98,7 @@
      * An emitted node
      */
     val node: Any,
-    box: Bounds,
+    box: IntBounds,
     data: Collection<Any?>,
     override val modifierInfo: List<ModifierInfo>,
     children: Collection<Group>
@@ -127,7 +121,7 @@
             else key
     }
 
-internal val emptyBox = Bounds(0, 0, 0, 0)
+internal val emptyBox = IntBounds(0, 0, 0, 0)
 
 /**
  * Iterate the slot table and extract a group tree that corresponds to the content of the table.
@@ -179,9 +173,9 @@
 }
 
 @OptIn(ExperimentalLayoutNodeApi::class)
-private fun boundsOfLayoutNode(node: LayoutNode): Bounds {
+private fun boundsOfLayoutNode(node: LayoutNode): IntBounds {
     if (node.owner == null) {
-        return Bounds(
+        return IntBounds(
             left = 0,
             top = 0,
             right = node.width,
@@ -194,7 +188,7 @@
     val top = position.y.roundToInt()
     val right = left + size.width
     val bottom = top + size.height
-    return Bounds(left = left, top = top, right = right, bottom = bottom)
+    return IntBounds(left = left, top = top, right = right, bottom = bottom)
 }
 
 /**
@@ -203,10 +197,10 @@
  */
 fun SlotTable.asTree(): Group = read { it.getGroup() }
 
-internal fun Bounds.union(other: Bounds): Bounds {
+internal fun IntBounds.union(other: IntBounds): IntBounds {
     if (this == emptyBox) return other else if (other == emptyBox) return this
 
-    return Bounds(
+    return IntBounds(
         left = min(left, other.left),
         top = min(top, other.top),
         bottom = max(bottom, other.bottom),
diff --git a/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt b/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
index 716672e..bad1460 100644
--- a/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
+++ b/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
@@ -42,12 +42,12 @@
 import androidx.ui.core.toAndroidRect
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.toArgb
-import androidx.ui.tooling.Bounds
 import androidx.ui.tooling.Group
 import androidx.ui.tooling.Inspectable
 import androidx.ui.tooling.SlotTableRecord
 import androidx.ui.tooling.asTree
 import androidx.ui.tooling.preview.animation.PreviewAnimationClock
+import androidx.ui.unit.IntBounds
 import androidx.ui.unit.PxBounds
 import androidx.ui.unit.toRect
 import kotlin.reflect.KClass
@@ -64,7 +64,7 @@
     val fileName: String,
     val lineNumber: Int,
     val methodName: String,
-    val bounds: Bounds,
+    val bounds: IntBounds,
     val children: List<ViewInfo>
 ) {
     fun hasBounds(): Boolean = bounds.bottom != 0 && bounds.right != 0
diff --git a/ui/ui-unit/api/0.1.0-dev15.txt b/ui/ui-unit/api/0.1.0-dev15.txt
index ddc351f..7fe4172 100644
--- a/ui/ui-unit/api/0.1.0-dev15.txt
+++ b/ui/ui-unit/api/0.1.0-dev15.txt
@@ -259,6 +259,29 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
+  @androidx.compose.Immutable public final class IntBounds {
+    ctor public IntBounds(int left, int top, int right, int bottom);
+    method public int component1();
+    method public int component2();
+    method public int component3();
+    method public int component4();
+    method @androidx.compose.Immutable public androidx.ui.unit.IntBounds copy(int left, int top, int right, int bottom);
+    method public int getBottom();
+    method public int getLeft();
+    method public int getRight();
+    method public int getTop();
+  }
+
+  public final class IntBoundsKt {
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntBounds IntBounds(androidx.ui.unit.IntOffset topLeft, androidx.ui.unit.IntSize size);
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntOffset center(androidx.ui.unit.IntBounds);
+    method public static inline int getHeight(androidx.ui.unit.IntBounds);
+    method public static inline int getWidth(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntBounds toBounds(androidx.ui.unit.IntSize);
+    method @androidx.compose.Stable public static androidx.ui.geometry.Rect toRect(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntSize toSize(androidx.ui.unit.IntBounds);
+  }
+
   @androidx.compose.Immutable public final class IntOffset {
     ctor public IntOffset(internal long value);
     method @androidx.compose.Stable public inline operator int component1();
diff --git a/ui/ui-unit/api/current.txt b/ui/ui-unit/api/current.txt
index ddc351f..7fe4172 100644
--- a/ui/ui-unit/api/current.txt
+++ b/ui/ui-unit/api/current.txt
@@ -259,6 +259,29 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
+  @androidx.compose.Immutable public final class IntBounds {
+    ctor public IntBounds(int left, int top, int right, int bottom);
+    method public int component1();
+    method public int component2();
+    method public int component3();
+    method public int component4();
+    method @androidx.compose.Immutable public androidx.ui.unit.IntBounds copy(int left, int top, int right, int bottom);
+    method public int getBottom();
+    method public int getLeft();
+    method public int getRight();
+    method public int getTop();
+  }
+
+  public final class IntBoundsKt {
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntBounds IntBounds(androidx.ui.unit.IntOffset topLeft, androidx.ui.unit.IntSize size);
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntOffset center(androidx.ui.unit.IntBounds);
+    method public static inline int getHeight(androidx.ui.unit.IntBounds);
+    method public static inline int getWidth(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntBounds toBounds(androidx.ui.unit.IntSize);
+    method @androidx.compose.Stable public static androidx.ui.geometry.Rect toRect(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntSize toSize(androidx.ui.unit.IntBounds);
+  }
+
   @androidx.compose.Immutable public final class IntOffset {
     ctor public IntOffset(internal long value);
     method @androidx.compose.Stable public inline operator int component1();
diff --git a/ui/ui-unit/api/public_plus_experimental_0.1.0-dev15.txt b/ui/ui-unit/api/public_plus_experimental_0.1.0-dev15.txt
index ddc351f..7fe4172 100644
--- a/ui/ui-unit/api/public_plus_experimental_0.1.0-dev15.txt
+++ b/ui/ui-unit/api/public_plus_experimental_0.1.0-dev15.txt
@@ -259,6 +259,29 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
+  @androidx.compose.Immutable public final class IntBounds {
+    ctor public IntBounds(int left, int top, int right, int bottom);
+    method public int component1();
+    method public int component2();
+    method public int component3();
+    method public int component4();
+    method @androidx.compose.Immutable public androidx.ui.unit.IntBounds copy(int left, int top, int right, int bottom);
+    method public int getBottom();
+    method public int getLeft();
+    method public int getRight();
+    method public int getTop();
+  }
+
+  public final class IntBoundsKt {
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntBounds IntBounds(androidx.ui.unit.IntOffset topLeft, androidx.ui.unit.IntSize size);
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntOffset center(androidx.ui.unit.IntBounds);
+    method public static inline int getHeight(androidx.ui.unit.IntBounds);
+    method public static inline int getWidth(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntBounds toBounds(androidx.ui.unit.IntSize);
+    method @androidx.compose.Stable public static androidx.ui.geometry.Rect toRect(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntSize toSize(androidx.ui.unit.IntBounds);
+  }
+
   @androidx.compose.Immutable public final class IntOffset {
     ctor public IntOffset(internal long value);
     method @androidx.compose.Stable public inline operator int component1();
diff --git a/ui/ui-unit/api/public_plus_experimental_current.txt b/ui/ui-unit/api/public_plus_experimental_current.txt
index ddc351f..7fe4172 100644
--- a/ui/ui-unit/api/public_plus_experimental_current.txt
+++ b/ui/ui-unit/api/public_plus_experimental_current.txt
@@ -259,6 +259,29 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
+  @androidx.compose.Immutable public final class IntBounds {
+    ctor public IntBounds(int left, int top, int right, int bottom);
+    method public int component1();
+    method public int component2();
+    method public int component3();
+    method public int component4();
+    method @androidx.compose.Immutable public androidx.ui.unit.IntBounds copy(int left, int top, int right, int bottom);
+    method public int getBottom();
+    method public int getLeft();
+    method public int getRight();
+    method public int getTop();
+  }
+
+  public final class IntBoundsKt {
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntBounds IntBounds(androidx.ui.unit.IntOffset topLeft, androidx.ui.unit.IntSize size);
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntOffset center(androidx.ui.unit.IntBounds);
+    method public static inline int getHeight(androidx.ui.unit.IntBounds);
+    method public static inline int getWidth(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntBounds toBounds(androidx.ui.unit.IntSize);
+    method @androidx.compose.Stable public static androidx.ui.geometry.Rect toRect(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntSize toSize(androidx.ui.unit.IntBounds);
+  }
+
   @androidx.compose.Immutable public final class IntOffset {
     ctor public IntOffset(internal long value);
     method @androidx.compose.Stable public inline operator int component1();
diff --git a/ui/ui-unit/api/restricted_0.1.0-dev15.txt b/ui/ui-unit/api/restricted_0.1.0-dev15.txt
index 293c6ed..a9c734a 100644
--- a/ui/ui-unit/api/restricted_0.1.0-dev15.txt
+++ b/ui/ui-unit/api/restricted_0.1.0-dev15.txt
@@ -259,6 +259,29 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
+  @androidx.compose.Immutable public final class IntBounds {
+    ctor public IntBounds(int left, int top, int right, int bottom);
+    method public int component1();
+    method public int component2();
+    method public int component3();
+    method public int component4();
+    method @androidx.compose.Immutable public androidx.ui.unit.IntBounds copy(int left, int top, int right, int bottom);
+    method public int getBottom();
+    method public int getLeft();
+    method public int getRight();
+    method public int getTop();
+  }
+
+  public final class IntBoundsKt {
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntBounds IntBounds(androidx.ui.unit.IntOffset topLeft, androidx.ui.unit.IntSize size);
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntOffset center(androidx.ui.unit.IntBounds);
+    method public static inline int getHeight(androidx.ui.unit.IntBounds);
+    method public static inline int getWidth(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntBounds toBounds(androidx.ui.unit.IntSize);
+    method @androidx.compose.Stable public static androidx.ui.geometry.Rect toRect(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntSize toSize(androidx.ui.unit.IntBounds);
+  }
+
   @androidx.compose.Immutable public final class IntOffset {
     ctor public IntOffset(internal long value);
     method @androidx.compose.Stable public inline operator int component1();
diff --git a/ui/ui-unit/api/restricted_current.txt b/ui/ui-unit/api/restricted_current.txt
index 293c6ed..a9c734a 100644
--- a/ui/ui-unit/api/restricted_current.txt
+++ b/ui/ui-unit/api/restricted_current.txt
@@ -259,6 +259,29 @@
     field public static final long SecondsPerMinute = 60L; // 0x3cL
   }
 
+  @androidx.compose.Immutable public final class IntBounds {
+    ctor public IntBounds(int left, int top, int right, int bottom);
+    method public int component1();
+    method public int component2();
+    method public int component3();
+    method public int component4();
+    method @androidx.compose.Immutable public androidx.ui.unit.IntBounds copy(int left, int top, int right, int bottom);
+    method public int getBottom();
+    method public int getLeft();
+    method public int getRight();
+    method public int getTop();
+  }
+
+  public final class IntBoundsKt {
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntBounds IntBounds(androidx.ui.unit.IntOffset topLeft, androidx.ui.unit.IntSize size);
+    method @androidx.compose.Stable public static inline androidx.ui.unit.IntOffset center(androidx.ui.unit.IntBounds);
+    method public static inline int getHeight(androidx.ui.unit.IntBounds);
+    method public static inline int getWidth(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntBounds toBounds(androidx.ui.unit.IntSize);
+    method @androidx.compose.Stable public static androidx.ui.geometry.Rect toRect(androidx.ui.unit.IntBounds);
+    method @androidx.compose.Stable public static androidx.ui.unit.IntSize toSize(androidx.ui.unit.IntBounds);
+  }
+
   @androidx.compose.Immutable public final class IntOffset {
     ctor public IntOffset(internal long value);
     method @androidx.compose.Stable public inline operator int component1();
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntBounds.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntBounds.kt
new file mode 100644
index 0000000..f2dd669
--- /dev/null
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntBounds.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.ui.unit
+
+import androidx.compose.Immutable
+import androidx.compose.Stable
+import androidx.ui.geometry.Rect
+
+/**
+ * A four dimensional bounds holder defined by integer pixels.
+ */
+@Immutable
+data class IntBounds(
+    val left: Int,
+    val top: Int,
+    val right: Int,
+    val bottom: Int
+)
+
+@Stable
+inline fun IntBounds(topLeft: IntOffset, size: IntSize) =
+    IntBounds(
+        left = topLeft.x,
+        top = topLeft.y,
+        right = topLeft.x + size.width,
+        bottom = topLeft.y + size.height
+    )
+
+/**
+ * The width of this IntBounds in integer pixels.
+ */
+@Stable
+inline val IntBounds.width: Int get() = right - left
+
+/**
+ * The height of this IntBounds in integer pixels.
+ */
+@Stable
+inline val IntBounds.height: Int get() = bottom - top
+
+/**
+ * Returns the [IntOffset] of the center of the [IntBounds].
+ */
+@Stable
+inline fun IntBounds.center(): IntOffset {
+    return IntOffset((left + right) / 2, (top + bottom) / 2)
+}
+
+/**
+ * Convert an [IntBounds] to an [IntSize].
+ */
+@Stable
+fun IntBounds.toSize(): IntSize {
+    return IntSize(width, height)
+}
+
+/**
+ * Convert an [IntSize] to an [IntBounds]. The left and top are 0 and the right and bottom
+ * are the width and height, respectively.
+ */
+@Stable
+fun IntSize.toBounds(): IntBounds {
+    return IntBounds(0, 0, width, height)
+}
+
+/**
+ * Convert an [IntBounds] to a [Rect].
+ */
+@Stable
+fun IntBounds.toRect(): Rect {
+    return Rect(
+        left.toFloat(),
+        top.toFloat(),
+        right.toFloat(),
+        bottom.toFloat()
+    )
+}
\ No newline at end of file
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt
index 9029a7c..58d04fa 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt
@@ -259,7 +259,7 @@
  */
 @Stable
 inline fun PxBounds.center(): Offset {
-    return Offset(left + width / 2f, top + height / 2f)
+    return Offset((left + right) / 2f, (top + bottom) / 2f)
 }
 
 /**
diff --git a/ui/ui-unit/src/test/kotlin/androidx/ui/unit/IntBoundsTest.kt b/ui/ui-unit/src/test/kotlin/androidx/ui/unit/IntBoundsTest.kt
new file mode 100644
index 0000000..e8c5d74
--- /dev/null
+++ b/ui/ui-unit/src/test/kotlin/androidx/ui/unit/IntBoundsTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.unit
+
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class IntBoundsTest {
+    @Test
+    fun boundsWidth() {
+        val bounds = IntBounds(10, 5, 25, 15)
+        Assert.assertEquals(15, bounds.width)
+    }
+
+    @Test
+    fun boundsHeight() {
+        val bounds = IntBounds(10, 5, 25, 15)
+        Assert.assertEquals(10, bounds.height)
+    }
+
+    @Test
+    fun toBounds() {
+        val size = IntSize(15, 10)
+        val bounds = IntBounds(0, 0, 15, 10)
+        Assert.assertEquals(bounds, size.toBounds())
+    }
+
+    @Test
+    fun toSize() {
+        val size = IntSize(15, 10)
+        val bounds = IntBounds(10, 5, 25, 15)
+        Assert.assertEquals(size, bounds.toSize())
+    }
+}
\ No newline at end of file