Do not shrink topology pane after a drag
Changing the height of the pane causes UI elements below it to shift around. Allow it to grow when needed but do not shrink once it has grown. Test: atest TopologyScaleTest.kt Flag: com.android.settings.flags.display_topology_pane_in_display_list Bug: b/352650922 Change-Id: I1a3e0ab77b05c5a4337e3e8ab865a974eb1faeda
This commit is contained in:
@@ -51,6 +51,10 @@ import kotlin.math.abs
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
// These extension methods make calls to min and max chainable.
|
||||||
|
fun Float.atMost(n: Number): Float = min(this, n.toFloat())
|
||||||
|
fun Float.atLeast(n: Number): Float = max(this, n.toFloat())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the parameters needed for transforming global display coordinates to and from topology
|
* Contains the parameters needed for transforming global display coordinates to and from topology
|
||||||
* pane coordinates. This is necessary for implementing an interactive display topology pane. The
|
* pane coordinates. This is necessary for implementing an interactive display topology pane. The
|
||||||
@@ -64,6 +68,9 @@ import kotlin.math.min
|
|||||||
* practice the origin will be the upper-left coordinate of the primary display.
|
* practice the origin will be the upper-left coordinate of the primary display.
|
||||||
*
|
*
|
||||||
* @param paneWidth width of the pane in view coordinates
|
* @param paneWidth width of the pane in view coordinates
|
||||||
|
* @param minPaneHeight smallest allowed height of the pane in view coordinates. This will not
|
||||||
|
* affect the block ratio, but only the final height of the pane and the
|
||||||
|
* position of the display bounds' center.
|
||||||
* @param minEdgeLength the smallest length permitted of a display block. This should be set based
|
* @param minEdgeLength the smallest length permitted of a display block. This should be set based
|
||||||
* on accessibility requirements, but also accounting for padding that appears
|
* on accessibility requirements, but also accounting for padding that appears
|
||||||
* around each button.
|
* around each button.
|
||||||
@@ -74,8 +81,8 @@ import kotlin.math.min
|
|||||||
* @param displaysPos the absolute topology coordinates for each display in the topology.
|
* @param displaysPos the absolute topology coordinates for each display in the topology.
|
||||||
*/
|
*/
|
||||||
class TopologyScale(
|
class TopologyScale(
|
||||||
paneWidth : Int, minEdgeLength : Int, maxBlockRatio : Float,
|
paneWidth: Int, minPaneHeight: Float, minEdgeLength: Int, maxBlockRatio: Float,
|
||||||
displaysPos : Collection<RectF>) {
|
displaysPos: Collection<RectF>) {
|
||||||
/** Scale of block sizes to real-world display sizes. Should be less than 1. */
|
/** Scale of block sizes to real-world display sizes. Should be less than 1. */
|
||||||
val blockRatio: Float
|
val blockRatio: Float
|
||||||
|
|
||||||
@@ -102,28 +109,30 @@ class TopologyScale(
|
|||||||
biggestDisplayHeight = max(biggestDisplayHeight, pos.height())
|
biggestDisplayHeight = max(biggestDisplayHeight, pos.height())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set height according to the width and the aspect ratio of the display bounds limitted by
|
// Set height according to the width and the aspect ratio of the display bounds limited by
|
||||||
// maxBlockRatio. It prevents blocks from being too large, which would make dragging and
|
// maxBlockRatio. It prevents blocks from being too large, which would make dragging and
|
||||||
// dropping awkward.
|
// dropping awkward.
|
||||||
val rawBlockRatio = min(maxBlockRatio, paneWidth.toFloat() * 0.6f / displayBounds.width())
|
blockRatio = maxBlockRatio
|
||||||
|
.atMost(paneWidth * 0.6 / displayBounds.width())
|
||||||
// If the `ratio` is set too low because one of the displays will have an edge less than
|
// If the `ratio` is set too low because one of the displays will have an edge less
|
||||||
// minEdgeLength(dp) long, increase it such that the smallest edge is that long.
|
// than minEdgeLength(dp) long, increase it such that the smallest edge is that
|
||||||
blockRatio = max(minEdgeLength.toFloat() / smallestDisplayDim, rawBlockRatio).toFloat()
|
// long.
|
||||||
|
.atLeast(minEdgeLength.toFloat() / smallestDisplayDim)
|
||||||
|
|
||||||
// Essentially, we just set the pane height based on the pre-determined pane width and the
|
// Essentially, we just set the pane height based on the pre-determined pane width and the
|
||||||
// aspect ratio of the display bounds. But we may need to increase it slightly to achieve
|
// aspect ratio of the display bounds.
|
||||||
// 20% padding above and below the display bounds - this is where the 0.6 comes from.
|
paneHeight = (paneWidth.toFloat() / displayBounds.width() * displayBounds.height())
|
||||||
val rawPaneHeight = max(
|
// We may need to increase it slightly to achieve 20% padding above and below the
|
||||||
paneWidth.toDouble() / displayBounds.width() * displayBounds.height(),
|
// display bounds - this is where the 0.6 comes from.
|
||||||
displayBounds.height() * blockRatio / 0.6).toFloat()
|
.atLeast(displayBounds.height() * blockRatio / 0.6)
|
||||||
|
|
||||||
// It is easy for the aspect ratio to result in an excessively tall pane, since the width is
|
// It is easy for the aspect ratio to result in an excessively tall pane, since the
|
||||||
// pre-determined and may be considerably wider than necessary. So we prevent the height
|
// width is pre-determined and may be considerably wider than necessary. So we
|
||||||
// from growing too large here, by limiting vertical padding to the size of the tallest
|
// prevent the height from growing too large here, by limiting vertical padding to
|
||||||
// display. This improves results for very tall display bounds.
|
// the size of the tallest display. This improves results for very tall display
|
||||||
paneHeight = min(
|
// bounds.
|
||||||
rawPaneHeight, blockRatio * (displayBounds.height() + biggestDisplayHeight * 2f))
|
.atMost(blockRatio * (displayBounds.height() + biggestDisplayHeight * 2f))
|
||||||
|
.atLeast(minPaneHeight)
|
||||||
|
|
||||||
// Set originPaneXY (the location of 0,0 in display space in the pane's coordinate system)
|
// Set originPaneXY (the location of 0,0 in display space in the pane's coordinate system)
|
||||||
// such that the display bounds rect is centered in the pane.
|
// such that the display bounds rect is centered in the pane.
|
||||||
@@ -365,7 +374,8 @@ class DisplayTopologyPreference(context : Context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
val scaling = TopologyScale(
|
val scaling = TopologyScale(
|
||||||
mPaneContent.width, minEdgeLength = 60, maxBlockRatio = 0.12f,
|
mPaneContent.width, minPaneHeight = mTopologyInfo?.scaling?.paneHeight ?: 0f,
|
||||||
|
minEdgeLength = 60, maxBlockRatio = 0.12f,
|
||||||
newBounds.map { it.second }.toList())
|
newBounds.map { it.second }.toList())
|
||||||
mPaneHolder.layoutParams.let {
|
mPaneHolder.layoutParams.let {
|
||||||
val newHeight = scaling.paneHeight.toInt()
|
val newHeight = scaling.paneHeight.toInt()
|
||||||
|
@@ -34,7 +34,7 @@ class TopologyScaleTest {
|
|||||||
@Test
|
@Test
|
||||||
fun oneDisplay4to3Aspect() {
|
fun oneDisplay4to3Aspect() {
|
||||||
val scale = TopologyScale(
|
val scale = TopologyScale(
|
||||||
/* paneWidth= */ 640,
|
/* paneWidth= */ 640, minPaneHeight = 0f,
|
||||||
minEdgeLength = 48, maxBlockRatio = 0.05f,
|
minEdgeLength = 48, maxBlockRatio = 0.05f,
|
||||||
listOf(RectF(0f, 0f, 640f, 480f)))
|
listOf(RectF(0f, 0f, 640f, 480f)))
|
||||||
|
|
||||||
@@ -47,12 +47,23 @@ class TopologyScaleTest {
|
|||||||
assertPointF(352f, 96f, 0.001f, scale.displayToPaneCoor(640f, 480f))
|
assertPointF(352f, 96f, 0.001f, scale.displayToPaneCoor(640f, 480f))
|
||||||
assertPointF(320f, 72f, 0.001f, scale.displayToPaneCoor(320f, 240f))
|
assertPointF(320f, 72f, 0.001f, scale.displayToPaneCoor(320f, 240f))
|
||||||
assertPointF(640f, 480f, 0.001f, scale.paneToDisplayCoor(352f, 96f))
|
assertPointF(640f, 480f, 0.001f, scale.paneToDisplayCoor(352f, 96f))
|
||||||
|
|
||||||
|
// Same as original scale but made taller with minPaneHeight.
|
||||||
|
// The paneHeight and origin coordinates are changed but the block ratio is the same.
|
||||||
|
val taller = TopologyScale(
|
||||||
|
/* paneWidth= */ 640, minPaneHeight = 155.0f,
|
||||||
|
minEdgeLength = 48, maxBlockRatio = 0.05f,
|
||||||
|
listOf(RectF(0f, 0f, 640f, 480f)))
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"{TopologyScale blockRatio=0.100000 originPaneXY=288.0,53.5 paneHeight=155.0}",
|
||||||
|
"" + taller)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun twoUnalignedDisplays() {
|
fun twoUnalignedDisplays() {
|
||||||
val scale = TopologyScale(
|
val scale = TopologyScale(
|
||||||
/* paneWidth= */ 300,
|
/* paneWidth= */ 300, minPaneHeight = 0f,
|
||||||
minEdgeLength = 48, maxBlockRatio = 0.05f,
|
minEdgeLength = 48, maxBlockRatio = 0.05f,
|
||||||
listOf(RectF(0f, 0f, 1920f, 1200f), RectF(1920f, -300f, 3840f, 900f)))
|
listOf(RectF(0f, 0f, 1920f, 1200f), RectF(1920f, -300f, 3840f, 900f)))
|
||||||
|
|
||||||
@@ -68,7 +79,7 @@ class TopologyScaleTest {
|
|||||||
@Test
|
@Test
|
||||||
fun twoDisplaysBlockRatioBumpedForGarSizeMinimumHorizontal() {
|
fun twoDisplaysBlockRatioBumpedForGarSizeMinimumHorizontal() {
|
||||||
val scale = TopologyScale(
|
val scale = TopologyScale(
|
||||||
/* paneWidth= */ 192,
|
/* paneWidth= */ 192, minPaneHeight = 0f,
|
||||||
minEdgeLength = 48, maxBlockRatio = 0.05f,
|
minEdgeLength = 48, maxBlockRatio = 0.05f,
|
||||||
listOf(RectF(0f, 0f, 240f, 320f), RectF(-240f, -320f, 0f, 0f)))
|
listOf(RectF(0f, 0f, 240f, 320f), RectF(-240f, -320f, 0f, 0f)))
|
||||||
|
|
||||||
@@ -86,7 +97,7 @@ class TopologyScaleTest {
|
|||||||
@Test
|
@Test
|
||||||
fun paneVerticalPaddingLimitedByTallestDisplay() {
|
fun paneVerticalPaddingLimitedByTallestDisplay() {
|
||||||
val scale = TopologyScale(
|
val scale = TopologyScale(
|
||||||
/* paneWidth= */ 300,
|
/* paneWidth= */ 300, minPaneHeight = 0f,
|
||||||
minEdgeLength = 48, maxBlockRatio = 0.05f,
|
minEdgeLength = 48, maxBlockRatio = 0.05f,
|
||||||
listOf(
|
listOf(
|
||||||
RectF(0f, 0f, 640f, 480f),
|
RectF(0f, 0f, 640f, 480f),
|
||||||
@@ -106,7 +117,7 @@ class TopologyScaleTest {
|
|||||||
@Test
|
@Test
|
||||||
fun limitedByCustomMaxBlockRatio() {
|
fun limitedByCustomMaxBlockRatio() {
|
||||||
val scale = TopologyScale(
|
val scale = TopologyScale(
|
||||||
/* paneWidth= */ 300,
|
/* paneWidth= */ 300, minPaneHeight = 0f,
|
||||||
minEdgeLength = 24, maxBlockRatio = 0.12f,
|
minEdgeLength = 24, maxBlockRatio = 0.12f,
|
||||||
listOf(
|
listOf(
|
||||||
RectF(0f, 0f, 640f, 480f),
|
RectF(0f, 0f, 640f, 480f),
|
||||||
@@ -123,7 +134,7 @@ class TopologyScaleTest {
|
|||||||
fun largeCustomMinEdgeLength() {
|
fun largeCustomMinEdgeLength() {
|
||||||
// minBlockEdgeLength/minDisplayEdgeLength = 80/480 = 1/6, so the block ratio will be 1/6
|
// minBlockEdgeLength/minDisplayEdgeLength = 80/480 = 1/6, so the block ratio will be 1/6
|
||||||
val scale = TopologyScale(
|
val scale = TopologyScale(
|
||||||
/* paneWidth= */ 300,
|
/* paneWidth= */ 300, minPaneHeight = 0f,
|
||||||
minEdgeLength = 80, maxBlockRatio = 0.05f,
|
minEdgeLength = 80, maxBlockRatio = 0.05f,
|
||||||
listOf(
|
listOf(
|
||||||
RectF(0f, 0f, 640f, 480f),
|
RectF(0f, 0f, 640f, 480f),
|
||||||
|
Reference in New Issue
Block a user