diff --git a/navi-uitron/src/main/java/com/navi/uitron/render/SliderRendererV2.kt b/navi-uitron/src/main/java/com/navi/uitron/render/SliderRendererV2.kt index e73fc68..826ffd9 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/render/SliderRendererV2.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/render/SliderRendererV2.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -22,10 +23,10 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.compositeOver -import androidx.compose.ui.layout.boundsInRoot import androidx.compose.ui.layout.layoutId import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.layout.positionInRoot +import androidx.compose.ui.layout.positionInParent +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import com.navi.uitron.model.action.MvelAction @@ -40,12 +41,12 @@ import com.navi.uitron.utils.MVEL_UUID import com.navi.uitron.utils.UUID_CONSTANT import com.navi.uitron.utils.VALUE import com.navi.uitron.viewmodel.UiTronViewModel +import dpToPx import getInputId import hexToComposeColor import orFalse import orTrue import orVal -import pxToDp import setHeight import setPadding import setWidth @@ -61,10 +62,14 @@ class SliderRendererV2( private val LEFT_PADDING = 16.dp private val DEFAULT_PADDING = 2.dp private val DEFAULT_TOOLTIP_VERTICAL_OFFSET = 6.dp + private val defaultHorizontalPadding by lazy { + dpToPx(10) + } private val TOOLTIP_ZINDEX = 1f private val DEFAULT_THUMB_WIDTH = 20.dp private val PREV_VALUE = "prev_value" + @Composable override fun Render( property: SliderProperty, @@ -77,13 +82,12 @@ class SliderRendererV2( var sliderUiTronData = uiTronData as? SliderDataV2 ?: return val steps = sliderUiTronData.stepValue?.size.orVal(1) - 1 - var absoluteSliderStartX by remember { mutableFloatStateOf(0f) } - var absoluteSliderEndX by remember { mutableFloatStateOf(0f) } val stepToValue = remember { mutableStateOf(mapOf()) } val mvelUuid = remember { mutableStateOf(0) } + var sliderStart by remember { mutableFloatStateOf(0f) } + var sliderEnd by remember { mutableFloatStateOf(0f) } var thumbX by remember { mutableFloatStateOf(0f) } - var thumbY by remember { mutableFloatStateOf(0f) } fun saveSelectionToHandle(position: Int?) { if (position == null || position < 0) return @@ -126,8 +130,8 @@ class SliderRendererV2( )?.toFloat() ?: 0f stepToValue.value = stepMap sliderPosition.value = positionIndex - thumbX = - absoluteSliderStartX + (positionIndex / steps) * (absoluteSliderEndX - absoluteSliderStartX) + + thumbX = sliderStart + (positionIndex / steps) * (sliderEnd - sliderStart) saveSelectionToHandle(positionIndex.toNearestInt()) } @@ -156,8 +160,8 @@ class SliderRendererV2( ) { Tooltip( thumbX = thumbX, - absoluteSliderEndX = absoluteSliderEndX, - absoluteSliderStartX = absoluteSliderStartX, + sliderStart = sliderStart, + sliderEnd = sliderEnd, tooltipView = property.tooltip ) Slider( @@ -165,7 +169,7 @@ class SliderRendererV2( onValueChange = { sliderPosition.value = it thumbX = - absoluteSliderStartX + (it / steps) * (absoluteSliderEndX - absoluteSliderStartX) + sliderStart + (it / steps) * (sliderEnd - sliderStart) updateValuesInMvelActionOnSliderChange( uiTronViewModel, sliderUiTronData, @@ -189,9 +193,9 @@ class SliderRendererV2( .rotate(property.rotation ?: 0f) .alpha(property.alpha ?: 1.0f) .onGloballyPositioned { coordinates -> - absoluteSliderStartX = (coordinates.boundsInRoot().left) - absoluteSliderEndX = (coordinates.boundsInRoot().right) - thumbY = coordinates.positionInRoot().y + val posInRoot = coordinates.positionInParent() + sliderStart = posInRoot.x + defaultHorizontalPadding + sliderEnd = posInRoot.x + coordinates.size.width.toFloat() - defaultHorizontalPadding }, colors = SliderDefaults.colors( thumbColor = property.sliderColors.thumbColor?.hexToComposeColor @@ -232,40 +236,52 @@ class SliderRendererV2( @Composable fun Tooltip( thumbX: Float, - absoluteSliderStartX: Float, - absoluteSliderEndX: Float, + sliderStart: Float, + sliderEnd: Float, tooltipView: UiTronView? ) { if (tooltipView == null) return val triangleHeight = 6.dp.toPx() val triangleWidth = 8.dp.toPx() - var toolTipViewHeight by remember { mutableFloatStateOf(0f) } - var toolTipViewWidth by remember { mutableFloatStateOf(0f) } - - var containerStartX = thumbX - LEFT_PADDING.toPx() - absoluteSliderStartX - val containerEndX = containerStartX + toolTipViewWidth + absoluteSliderStartX - - containerStartX = if (containerStartX <= 0) { - -LEFT_PADDING.toPx() + 5F - } else if (containerEndX >= (absoluteSliderEndX + 2 * LEFT_PADDING.toPx())) { - absoluteSliderEndX - toolTipViewWidth - absoluteSliderStartX + LEFT_PADDING.toPx() - DEFAULT_PADDING.toPx() - } else { - thumbX - absoluteSliderStartX - toolTipViewWidth / 2 - } + var height by remember { mutableIntStateOf(0) } + var width by remember { mutableIntStateOf(0) } Column(modifier = Modifier .wrapContentSize() - .offset(x = pxToDp(containerStartX).dp, y = DEFAULT_TOOLTIP_VERTICAL_OFFSET) + .offset { + IntOffset( + if ( + !isToolTipOverFlowing(thumbX, width, sliderStart, sliderEnd) + ) { + thumbX.toInt() - width / 2 + } else if (isToolTipOverFlowingTowardsLeft(thumbX, width, sliderEnd)) { + (sliderEnd + defaultHorizontalPadding).toInt() - width + } else { + (sliderStart - defaultHorizontalPadding).toInt() + }, + DEFAULT_TOOLTIP_VERTICAL_OFFSET + .toPx() + .toInt() + ) + } .zIndex(TOOLTIP_ZINDEX) .onGloballyPositioned { coordinates -> - toolTipViewHeight = coordinates.size.height.toFloat() - toolTipViewWidth = coordinates.size.width.toFloat() + height = coordinates.size.height + width = coordinates.size.width } ) { uiTronRenderer.Render(composeViews = listOf(tooltipView)) InvertedTriangle( - triangleStartX = (toolTipViewWidth - triangleWidth) / 2, + triangleStartX = if ( + !isToolTipOverFlowing(thumbX, width, sliderStart, sliderEnd) + ) { + (width - triangleWidth) / 2 + } else if (isToolTipOverFlowingTowardsLeft(thumbX, width, sliderEnd)) { + thumbX - ((sliderEnd + defaultHorizontalPadding).toInt() - width) - triangleWidth / 2 + } else { + thumbX - triangleWidth / 2 + }, triangleColorCode = tooltipView.property?.backgroundColor.orEmpty(), triangleWidth = triangleWidth, triangleHeight = triangleHeight @@ -304,6 +320,13 @@ class SliderRendererV2( } } + private fun isToolTipOverFlowing(thumbX: Float, width: Int, sliderStart: Float, sliderEnd: Float) = + (thumbX.toInt() - width / 2 > sliderStart - defaultHorizontalPadding && + thumbX.toInt() - width / 2 < sliderEnd + defaultHorizontalPadding - width).not() + + private fun isToolTipOverFlowingTowardsLeft(thumbX: Float, width: Int, sliderEnd: Float) = + thumbX.toInt() - width / 2 >= sliderEnd + defaultHorizontalPadding - width + private fun updateValuesInMvelActionOnSliderChange( uiTronViewModel: UiTronViewModel, sliderData: SliderDataV2,