mirror of
https://github.com/BetterAndroid/FlexiUI.git
synced 2025-09-09 20:14:18 +08:00
refactor: make TabPosition opened and use TabRow to manage TabIndicator
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
*
|
*
|
||||||
* This file is created by fankes on 2023/11/9.
|
* This file is created by fankes on 2023/11/9.
|
||||||
*/
|
*/
|
||||||
@file:Suppress("unused")
|
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
package com.highcapable.flexiui.component
|
package com.highcapable.flexiui.component
|
||||||
|
|
||||||
@@ -102,25 +102,7 @@ fun TabRow(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
colors: TabColors = Tab.colors,
|
colors: TabColors = Tab.colors,
|
||||||
style: TabStyle = Tab.style,
|
style: TabStyle = Tab.style,
|
||||||
tabs: @Composable () -> Unit
|
indicator: @Composable TabRow.() -> Unit = { TabIndicator(modifier = Modifier.tabIndicatorOffset()) },
|
||||||
) {
|
|
||||||
TabRow(
|
|
||||||
selectedTabIndex = selectedTabIndex,
|
|
||||||
modifier = modifier,
|
|
||||||
colors = colors,
|
|
||||||
style = style,
|
|
||||||
pagerState = null,
|
|
||||||
tabs = tabs
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun TabRow(
|
|
||||||
selectedTabIndex: Int = 0,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
colors: TabColors = Tab.colors,
|
|
||||||
style: TabStyle = Tab.style,
|
|
||||||
pagerState: PagerState?,
|
|
||||||
tabs: @Composable () -> Unit
|
tabs: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
TabStyleBox(modifier, colors, style) {
|
TabStyleBox(modifier, colors, style) {
|
||||||
@@ -145,7 +127,7 @@ fun TabRow(
|
|||||||
placeable.placeRelative(x = index * tabAverageWidth, y = 0)
|
placeable.placeRelative(x = index * tabAverageWidth, y = 0)
|
||||||
}
|
}
|
||||||
subcompose(TabSlots.Indicator) {
|
subcompose(TabSlots.Indicator) {
|
||||||
TabIndicator(selectedTabIndex, colors, style, pagerState, tabPositions)
|
indicator(TabRow(selectedTabIndex, colors, style, tabPositions))
|
||||||
}.forEach {
|
}.forEach {
|
||||||
it.measure(Constraints.fixed(tabRowWidth, tabRowHeight)).placeRelative(x = 0, y = 0)
|
it.measure(Constraints.fixed(tabRowWidth, tabRowHeight)).placeRelative(x = 0, y = 0)
|
||||||
}
|
}
|
||||||
@@ -161,27 +143,7 @@ fun ScrollableTabRow(
|
|||||||
colors: TabColors = Tab.colors,
|
colors: TabColors = Tab.colors,
|
||||||
style: TabStyle = Tab.style,
|
style: TabStyle = Tab.style,
|
||||||
scrollState: ScrollState = rememberScrollState(),
|
scrollState: ScrollState = rememberScrollState(),
|
||||||
tabs: @Composable () -> Unit
|
indicator: @Composable TabRow.() -> Unit = { TabIndicator(modifier = Modifier.tabIndicatorOffset()) },
|
||||||
) {
|
|
||||||
ScrollableTabRow(
|
|
||||||
selectedTabIndex = selectedTabIndex,
|
|
||||||
modifier = modifier,
|
|
||||||
colors = colors,
|
|
||||||
style = style,
|
|
||||||
pagerState = null,
|
|
||||||
scrollState = scrollState,
|
|
||||||
tabs = tabs
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ScrollableTabRow(
|
|
||||||
selectedTabIndex: Int = 0,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
colors: TabColors = Tab.colors,
|
|
||||||
style: TabStyle = Tab.style,
|
|
||||||
pagerState: PagerState?,
|
|
||||||
scrollState: ScrollState = rememberScrollState(),
|
|
||||||
tabs: @Composable () -> Unit
|
tabs: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
TabStyleBox(modifier, colors, style) {
|
TabStyleBox(modifier, colors, style) {
|
||||||
@@ -214,7 +176,7 @@ fun ScrollableTabRow(
|
|||||||
tabLeft += placeables.width
|
tabLeft += placeables.width
|
||||||
}
|
}
|
||||||
subcompose(TabSlots.Indicator) {
|
subcompose(TabSlots.Indicator) {
|
||||||
TabIndicator(selectedTabIndex, colors, style, pagerState, tabPositions)
|
indicator(TabRow(selectedTabIndex, colors, style, tabPositions))
|
||||||
}.forEach {
|
}.forEach {
|
||||||
it.measure(Constraints.fixed(layoutWidth, layoutHeight)).placeRelative(x = 0, y = 0)
|
it.measure(Constraints.fixed(layoutWidth, layoutHeight)).placeRelative(x = 0, y = 0)
|
||||||
}
|
}
|
||||||
@@ -271,23 +233,6 @@ fun Tab(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun TabIndicator(
|
|
||||||
selectedTabIndex: Int,
|
|
||||||
colors: TabColors,
|
|
||||||
style: TabStyle,
|
|
||||||
pagerState: PagerState?,
|
|
||||||
tabPositions: List<TabPosition>
|
|
||||||
) {
|
|
||||||
val indicatorModifier = pagerState?.let { Modifier.pagerTabIndicatorOffset(it, tabPositions, style.indicatorWidth) }
|
|
||||||
?: Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex], style.indicatorWidth)
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.then(indicatorModifier)
|
|
||||||
.height(style.indicatorHeight)
|
|
||||||
.background(colors.indicatorColor, style.indicatorShape)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TabStyleBox(
|
private fun TabStyleBox(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
@@ -311,16 +256,42 @@ private fun rememberScrollableTabData(scrollState: ScrollState): ScrollableTabDa
|
|||||||
return remember(scrollState, coroutineScope) { ScrollableTabData(scrollState, coroutineScope) }
|
return remember(scrollState, coroutineScope) { ScrollableTabData(scrollState, coroutineScope) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Modifier.tabIndicatorOffset(
|
@Immutable
|
||||||
currentTabPosition: TabPosition,
|
data class TabPosition(val left: Dp, val width: Dp, val tabWidth: Dp) {
|
||||||
indicatorWidth: Dp
|
|
||||||
) = composed(
|
val right get() = left + width
|
||||||
|
|
||||||
|
fun calculateCenter(currentWidth: Dp) = left + width / 2 - currentWidth / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class TabRow internal constructor(
|
||||||
|
val selectedTabIndex: Int,
|
||||||
|
val colors: TabColors,
|
||||||
|
val style: TabStyle,
|
||||||
|
val tabPositions: List<TabPosition>
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TabIndicator(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
color: Color = colors.indicatorColor,
|
||||||
|
height: Dp = style.indicatorHeight,
|
||||||
|
shape: Shape = style.indicatorShape
|
||||||
|
) {
|
||||||
|
Box(modifier.height(height).background(color, shape))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Modifier.tabIndicatorOffset(
|
||||||
|
currentTabPosition: TabPosition = tabPositions[selectedTabIndex],
|
||||||
|
indicatorWidth: Dp = style.indicatorWidth
|
||||||
|
) = composed(
|
||||||
inspectorInfo = debugInspectorInfo {
|
inspectorInfo = debugInspectorInfo {
|
||||||
name = "tabIndicatorOffset"
|
name = "tabIndicatorOffset"
|
||||||
properties["currentTabPosition"] = currentTabPosition
|
properties["currentTabPosition"] = currentTabPosition
|
||||||
properties["indicatorWidth"] = indicatorWidth
|
properties["indicatorWidth"] = indicatorWidth
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
val currentWidth = indicatorWidth.orElse() ?: currentTabPosition.tabWidth
|
val currentWidth = indicatorWidth.orElse() ?: currentTabPosition.tabWidth
|
||||||
val animatedWidh by animateDpAsState(
|
val animatedWidh by animateDpAsState(
|
||||||
targetValue = currentWidth,
|
targetValue = currentWidth,
|
||||||
@@ -334,20 +305,20 @@ private fun Modifier.tabIndicatorOffset(
|
|||||||
.wrapContentSize(Alignment.BottomStart)
|
.wrapContentSize(Alignment.BottomStart)
|
||||||
.offset(x = animatedOffsetX)
|
.offset(x = animatedOffsetX)
|
||||||
.width(animatedWidh)
|
.width(animatedWidh)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Modifier.pagerTabIndicatorOffset(
|
fun Modifier.pagerTabIndicatorOffset(
|
||||||
pagerState: PagerState,
|
pagerState: PagerState,
|
||||||
tabPositions: List<TabPosition>,
|
tabPositions: List<TabPosition> = this@TabRow.tabPositions,
|
||||||
indicatorWidth: Dp
|
indicatorWidth: Dp = style.indicatorWidth
|
||||||
) = composed(
|
) = composed(
|
||||||
inspectorInfo = debugInspectorInfo {
|
inspectorInfo = debugInspectorInfo {
|
||||||
name = "pagerTabIndicatorOffset"
|
name = "pagerTabIndicatorOffset"
|
||||||
properties["pagerState"] = pagerState
|
properties["pagerState"] = pagerState
|
||||||
properties["tabPositions"] = tabPositions
|
properties["tabPositions"] = tabPositions
|
||||||
properties["indicatorWidth"] = indicatorWidth
|
properties["indicatorWidth"] = indicatorWidth
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
layout { measurable, constraints ->
|
layout { measurable, constraints ->
|
||||||
// If there are no pages, nothing to show.
|
// If there are no pages, nothing to show.
|
||||||
if (tabPositions.isEmpty()) return@layout layout(constraints.maxWidth, 0) {}
|
if (tabPositions.isEmpty()) return@layout layout(constraints.maxWidth, 0) {}
|
||||||
@@ -386,6 +357,7 @@ private fun Modifier.pagerTabIndicatorOffset(
|
|||||||
val measureHeight = maxOf(placeable.height, constraints.minHeight)
|
val measureHeight = maxOf(placeable.height, constraints.minHeight)
|
||||||
layout(measureWidth, measureHeight) { placeable.placeRelative(movableOffsetX, offsetY) }
|
layout(measureWidth, measureHeight) { placeable.placeRelative(movableOffsetX, offsetY) }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
@@ -433,14 +405,6 @@ private class ScrollableTabData(private val scrollState: ScrollState, private va
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
|
||||||
private data class TabPosition(val left: Dp, val width: Dp, val tabWidth: Dp) {
|
|
||||||
|
|
||||||
val right get() = left + width
|
|
||||||
|
|
||||||
fun calculateCenter(currentWidth: Dp) = left + width / 2 - currentWidth / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
private enum class TabSlots { Tabs, TabsAverage, Indicator }
|
private enum class TabSlots { Tabs, TabsAverage, Indicator }
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user