diff --git a/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsContainer.kt b/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsContainer.kt index 71fe97fbf2..125c4dec0e 100644 --- a/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsContainer.kt +++ b/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsContainer.kt @@ -106,6 +106,7 @@ class TitleAndButtonsContainer(context: Context) : ViewGroup(context) { override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { val titleComponent = getTitleComponent() val isCenter = titleComponentAlignment == Alignment.Center + val isFill = titleComponentAlignment == Alignment.Fill val containerWidth = r - l val containerHeight = b - t val isRTL = isRTL() @@ -114,7 +115,7 @@ class TitleAndButtonsContainer(context: Context) : ViewGroup(context) { val leftBarWidth = leftButtonBar.measuredWidth val rightBarWidth = rightButtonBar.measuredWidth - val (titleLeft, titleRight) = resolveHorizontalTitleBoundsLimit(containerWidth, titleWidth, leftBarWidth, rightBarWidth, isCenter, isRTL) + val (titleLeft, titleRight) = resolveHorizontalTitleBoundsLimit(containerWidth, titleWidth, leftBarWidth, rightBarWidth, isCenter, isRTL, isFill) val (titleTop, titleBottom) = resolveVerticalTitleBoundsLimit(containerHeight, titleHeight) val (leftButtonsLeft, leftButtonsRight) = resolveLeftButtonsBounds(containerWidth, leftBarWidth, isRTL) val (rightButtonsLeft, rightButtonsRight) = resolveRightButtonsBounds(containerWidth, rightButtonBar.measuredWidth, isRTL) diff --git a/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsMeasurer.kt b/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsMeasurer.kt index 260a698a1a..7a873429f8 100644 --- a/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsMeasurer.kt +++ b/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsMeasurer.kt @@ -19,12 +19,14 @@ fun makeTitleAtMostWidthMeasureSpec(containerWidth: Int, rightBarWidth: Int, lef return if (isCenter) { val availableWidth = containerWidth - rightBarWidth - leftBarWidth View.MeasureSpec.makeMeasureSpec(availableWidth, View.MeasureSpec.AT_MOST) + } else if (isFill) { + // Fill alignment must span the entire space between left and right button bars, + // so do not reserve the DEFAULT_LEFT_MARGIN_PX padding used for left-aligned titles. + val availableWidth = containerWidth - rightBarWidth - leftBarWidth + View.MeasureSpec.makeMeasureSpec(availableWidth, View.MeasureSpec.EXACTLY) } else { val availableWidth = containerWidth - rightBarWidth - leftBarWidth - 2 * DEFAULT_LEFT_MARGIN_PX - View.MeasureSpec.makeMeasureSpec( - availableWidth, - if (isFill) View.MeasureSpec.EXACTLY else View.MeasureSpec.AT_MOST - ) + View.MeasureSpec.makeMeasureSpec(availableWidth, View.MeasureSpec.AT_MOST) } } @@ -41,7 +43,8 @@ fun resolveHorizontalTitleBoundsLimit( leftBarWidth: Int, rightBarWidth: Int, isCenter: Boolean, - isRTL: Boolean + isRTL: Boolean, + isFill: Boolean = false ): Pair { val resolvedLeftBarWidth = if (isRTL) rightBarWidth else leftBarWidth val resolvedRightBarWidth = if (isRTL) leftBarWidth else rightBarWidth @@ -49,6 +52,13 @@ fun resolveHorizontalTitleBoundsLimit( var suggestedRight: TitleRight val rightLimit = parentWidth - resolvedRightBarWidth + if (isFill) { + // Fill alignment must span the entire space between the left and right + // button bars without the DEFAULT_LEFT_MARGIN_PX reservation used for + // left-aligned titles. Mirrors the measure spec produced by + // makeTitleAtMostWidthMeasureSpec when isFill = true. + return resolvedLeftBarWidth to rightLimit + } if (isCenter) { val availableSpace = parentWidth - resolvedLeftBarWidth - resolvedRightBarWidth if (titleWidth >= availableSpace) { diff --git a/android/src/test/java/com/reactnativenavigation/utils/TitleAndButtonsMeasurer.kt b/android/src/test/java/com/reactnativenavigation/utils/TitleAndButtonsMeasurer.kt index 50ead44aed..76cd28e02c 100644 --- a/android/src/test/java/com/reactnativenavigation/utils/TitleAndButtonsMeasurer.kt +++ b/android/src/test/java/com/reactnativenavigation/utils/TitleAndButtonsMeasurer.kt @@ -1,7 +1,9 @@ package com.reactnativenavigation.utils +import android.view.View import com.reactnativenavigation.BaseRobolectricTest import com.reactnativenavigation.views.stack.topbar.titlebar.DEFAULT_LEFT_MARGIN_PX +import com.reactnativenavigation.views.stack.topbar.titlebar.makeTitleAtMostWidthMeasureSpec import com.reactnativenavigation.views.stack.topbar.titlebar.resolveHorizontalTitleBoundsLimit import com.reactnativenavigation.views.stack.topbar.titlebar.resolveLeftButtonsBounds import com.reactnativenavigation.views.stack.topbar.titlebar.resolveRightButtonsBounds @@ -355,4 +357,82 @@ class TitleAndButtonsMeasurer : BaseRobolectricTest() { assertEquals(parentWidth - leftButtons - DEFAULT_LEFT_MARGIN_PX - barWidth - DEFAULT_LEFT_MARGIN_PX, left) assertEquals(parentWidth - leftButtons - DEFAULT_LEFT_MARGIN_PX, right) } -} \ No newline at end of file + + // ----- makeTitleAtMostWidthMeasureSpec ----- + + @Test + fun `makeTitleAtMostWidthMeasureSpec - Fill - no buttons - EXACTLY full container width`() { + val spec = makeTitleAtMostWidthMeasureSpec(parentWidth, 0, 0, isCenter = false, isFill = true) + assertEquals(View.MeasureSpec.EXACTLY, View.MeasureSpec.getMode(spec)) + assertEquals(parentWidth, View.MeasureSpec.getSize(spec)) + } + + @Test + fun `makeTitleAtMostWidthMeasureSpec - Fill - with buttons - EXACTLY width minus bars`() { + val leftBar = 120 + val rightBar = 80 + val spec = makeTitleAtMostWidthMeasureSpec(parentWidth, rightBar, leftBar, isCenter = false, isFill = true) + assertEquals(View.MeasureSpec.EXACTLY, View.MeasureSpec.getMode(spec)) + assertEquals(parentWidth - leftBar - rightBar, View.MeasureSpec.getSize(spec)) + } + + @Test + fun `makeTitleAtMostWidthMeasureSpec - Center - AT_MOST full container width minus bars`() { + val leftBar = 100 + val rightBar = 100 + val spec = makeTitleAtMostWidthMeasureSpec(parentWidth, rightBar, leftBar, isCenter = true) + assertEquals(View.MeasureSpec.AT_MOST, View.MeasureSpec.getMode(spec)) + assertEquals(parentWidth - leftBar - rightBar, View.MeasureSpec.getSize(spec)) + } + + @Test + fun `makeTitleAtMostWidthMeasureSpec - default (left-aligned) - keeps 2 x DEFAULT_LEFT_MARGIN_PX reservation`() { + val leftBar = 100 + val rightBar = 100 + val spec = makeTitleAtMostWidthMeasureSpec(parentWidth, rightBar, leftBar, isCenter = false, isFill = false) + assertEquals(View.MeasureSpec.AT_MOST, View.MeasureSpec.getMode(spec)) + assertEquals(parentWidth - leftBar - rightBar - 2 * DEFAULT_LEFT_MARGIN_PX, View.MeasureSpec.getSize(spec)) + } + + // ----- resolveHorizontalTitleBoundsLimit - Fill alignment ----- + + @Test + fun `Fill - no buttons - Title should span full container width`() { + val barWidth = 200 // measured width of the title component + val leftButtons = 0 + val rightButtons = 0 + val isRTL = false + val center = false + val isFill = true + val (left, right) = resolveHorizontalTitleBoundsLimit(parentWidth, barWidth, leftButtons, rightButtons, center, isRTL, isFill) + assertEquals(0, left) + assertEquals(parentWidth, right) + } + + @Test + fun `Fill - with buttons - Title should span between buttons without DEFAULT_LEFT_MARGIN_PX`() { + val barWidth = 200 + val leftButtons = 120 + val rightButtons = 80 + val isRTL = false + val center = false + val isFill = true + val (left, right) = resolveHorizontalTitleBoundsLimit(parentWidth, barWidth, leftButtons, rightButtons, center, isRTL, isFill) + assertEquals(leftButtons, left) + assertEquals(parentWidth - rightButtons, right) + } + + @Test + fun `RTL - Fill - with buttons - Title should span between buttons after RTL flip`() { + val barWidth = 200 + val leftButtons = 120 + val rightButtons = 80 + val isRTL = true + val center = false + val isFill = true + val (left, right) = resolveHorizontalTitleBoundsLimit(parentWidth, barWidth, leftButtons, rightButtons, center, isRTL, isFill) + // RTL flips logical left/right: leftButtons render on the right edge, rightButtons on the left. + assertEquals(rightButtons, left) + assertEquals(parentWidth - leftButtons, right) + } +} diff --git a/android/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt b/android/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt index a192738fb3..46523a3f20 100644 --- a/android/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt +++ b/android/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt @@ -352,8 +352,11 @@ class TitleAndButtonsContainerTest : BaseTest() { ) component = uut.getTitleComponent() idleMainLooper() - assertThat(component.left).isEqualTo(DEFAULT_LEFT_MARGIN_PX) - assertThat(component.right).isEqualTo(titleBarWidth + 2 * DEFAULT_LEFT_MARGIN_PX) + // Fill alignment must span the full container width between left and right + // button bars (zero-width here). The previous assertion that allowed the + // DEFAULT_LEFT_MARGIN_PX reservation matched left-aligned behaviour, not Fill. + assertThat(component.left).isEqualTo(0) + assertThat(component.right).isEqualTo(UUT_WIDTH) } @Test