Document TextPicker Composable and it's util classes, functions
This commit is contained in:
parent
be18d9859d
commit
ef15aca31f
15 changed files with 311 additions and 45 deletions
|
|
@ -46,8 +46,8 @@ fun NumberPickerScope.LinedInnerTextPicker(modifier: Modifier = Modifier) {
|
|||
modifier = modifier,
|
||||
textForIndex = textForIndex,
|
||||
itemCount = itemCount,
|
||||
selected = selectedIndex,
|
||||
onSelectedChange = onSelectedIndexChange,
|
||||
selectedIndex = selectedIndex,
|
||||
onSelectedIndexChange = onSelectedIndexChange,
|
||||
onIndexDifferenceChanging = onIndexDifferenceChanging,
|
||||
state = state
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,17 +11,45 @@ import org.fknives.android.compose.picker.text.util.TextPickerDefaults
|
|||
import org.fknives.android.compose.picker.text.util.rememberTextPickerAnimator
|
||||
import org.fknives.android.compose.picker.text.util.rememberTextPickerState
|
||||
|
||||
/**
|
||||
* Customised version of TextPicker.
|
||||
* Adds additional Dividers by [textPickerContent] and places them by customized [textPickerMeasurePolicy]
|
||||
* The Default Dividers can be customised by giving parameters to [LinedTextPickerContent] as [textPickerContent].
|
||||
*
|
||||
* If no selection changes, snaps back to the selected element.
|
||||
* Longer drags and letting go, animates scrolling over elements.
|
||||
* Remembers the previously selected element and when selecting programmatically, scrolls to it..
|
||||
* (Selected by UI action makes it previously selected before sending back the event, aka no double animation)
|
||||
*
|
||||
* @param modifier Standard Modifier for the Layout
|
||||
* @param textForIndex Used to display Text for the given index. Indexes are between `0 until [itemCount]`.
|
||||
* @param itemCount Number of Elements that can be selected
|
||||
* @param selectedIndex Currently selected index. Must be between `0 until [itemCount]`
|
||||
* @param onSelectedIndexChange Notified after animation, what the User selected through dragging of the UI.
|
||||
* @param textStyle Default textStyle provided for the Text Composables inside the Layout.
|
||||
* @param roundAround Behavioural change:
|
||||
* Setting this to True, means the elements behave like a wheel, and the last element is above the first.
|
||||
* Setting this to False, means the first element has no elements above it, and the last element has no element below it.
|
||||
* @param onIndexDifferenceChanging Signals the animation changes, how much the current dragging is away from [selectedIndex].
|
||||
* Negative values mean the index were decreased, Positive means it was increased.
|
||||
* @param state Animation State for the TextPicker
|
||||
* @param animator Uses state to Animate the Composable elements. Handles continous drag, calculating fling and snapping to an index.
|
||||
* @param textPickerContent The actual Composables inside the TextPicker, by default it is the 4 Texts and the 2 Dividers
|
||||
* @param textPickerMeasurePolicy The MeasurePolicy of the Layout, by default Translates the 4 Texts in [textPickerContent]
|
||||
* according to [state] changed by [animator]
|
||||
* @param pickerItem The Expected Text Composables in [textPickerContent].
|
||||
*/
|
||||
@Composable
|
||||
fun LinedTextPicker(
|
||||
modifier: Modifier = Modifier,
|
||||
textForIndex: (Int) -> String,
|
||||
itemCount: Int,
|
||||
selected: Int,
|
||||
onSelectedChange: (Int) -> Unit,
|
||||
selectedIndex: Int,
|
||||
onSelectedIndexChange: (Int) -> Unit,
|
||||
textStyle: TextStyle = LocalTextStyle.current,
|
||||
roundAround: Boolean = TextPickerDefaults.roundAround,
|
||||
onIndexDifferenceChanging: (Int) -> Unit = TextPickerDefaults.onIndexDifferenceChanging,
|
||||
state: TextPickerState = rememberTextPickerState(selected = selected, itemCount = itemCount),
|
||||
state: TextPickerState = rememberTextPickerState(selected = selectedIndex, itemCount = itemCount),
|
||||
animator: TextPickerAnimator = rememberTextPickerAnimator(roundAround = roundAround, onIndexDifferenceChanging = onIndexDifferenceChanging),
|
||||
textPickerContent: TextPickerContent = LinedTextPickerContent(),
|
||||
textPickerMeasurePolicy: MeasurePolicy = remember(state) { LinedTextPickerMeasurePolicy(state) },
|
||||
|
|
@ -31,8 +59,8 @@ fun LinedTextPicker(
|
|||
modifier = modifier,
|
||||
textForIndex = textForIndex,
|
||||
itemCount = itemCount,
|
||||
selectedIndex = selected,
|
||||
onSelectedIndexChange = onSelectedChange,
|
||||
selectedIndex = selectedIndex,
|
||||
onSelectedIndexChange = onSelectedIndexChange,
|
||||
textStyle = textStyle,
|
||||
roundAround = roundAround,
|
||||
onIndexDifferenceChanging = onIndexDifferenceChanging,
|
||||
|
|
|
|||
|
|
@ -15,13 +15,48 @@ import androidx.compose.ui.layout.MeasurePolicy
|
|||
import androidx.compose.ui.text.TextStyle
|
||||
import org.fknives.android.compose.picker.text.content.DefaultTextPickerContent
|
||||
import org.fknives.android.compose.picker.text.content.DefaultTextPickerMeasurePolicy
|
||||
import org.fknives.android.compose.picker.text.content.TextPickerContent
|
||||
import org.fknives.android.compose.picker.text.content.defaultNumberPickerTextAlphaModifier
|
||||
import org.fknives.android.compose.picker.text.util.TextPickerDefaults
|
||||
import org.fknives.android.compose.picker.text.util.rememberDefaultMoveUnsafeToProperIndex
|
||||
import org.fknives.android.compose.picker.text.util.rememberTextPickerAnimator
|
||||
import org.fknives.android.compose.picker.text.util.rememberTextPickerState
|
||||
import org.fknives.android.compose.picker.text.util.rememberWrappedTextForIndex
|
||||
import org.fknives.android.compose.picker.text.util.rememberSafeTextForIndex
|
||||
|
||||
/**
|
||||
* NumberPicker Custom Layout Composable which shows given text on each index.
|
||||
* Shows 3 selections options (while animating may show 4).
|
||||
* Scrolling the Composable moves between elements and at the end of the movement is selects the most middle element.
|
||||
*
|
||||
* If no selection changes, snaps back to the selected element.
|
||||
* Longer drags and letting go, animates scrolling over elements.
|
||||
* Remembers the previously selected element and when selecting programmatically, scrolls to it..
|
||||
* (Selected by UI action makes it previously selected before sending back the event, aka no double animation)
|
||||
*
|
||||
* @param modifier Standard Modifier for the Layout
|
||||
* @param textForIndex Used to display Text for the given index. Indexes are between `0 until [itemCount]`.
|
||||
* @param itemCount Number of Elements that can be selected
|
||||
* @param selectedIndex Currently selected index. Must be between `0 until [itemCount]`
|
||||
* @param onSelectedIndexChange Notified after animation, what the User selected through dragging of the UI.
|
||||
* @param textStyle Default textStyle provided for the Text Composables inside the Layout.
|
||||
* @param roundAround Behavioural change:
|
||||
* Setting this to True, means the elements behave like a wheel, and the last element is above the first.
|
||||
* Setting this to False, means the first element has no elements above it, and the last element has no element below it.
|
||||
* @param onIndexDifferenceChanging Signals the animation changes, how much the current dragging is away from [selectedIndex].
|
||||
* Negative values mean the index were decreased, Positive means it was increased.
|
||||
* @param state Animation State for the TextPicker
|
||||
* @param animator Uses state to Animate the Composable elements. Handles continous drag, calculating fling and snapping to an index.
|
||||
* @param textPickerContent The actual Composables inside the TextPicker, by default it is the 4 Texts
|
||||
* @param textPickerMeasurePolicy The MeasurePolicy of the Layout, by default Translates the 4 Texts in [textPickerContent]
|
||||
* according to [state] changed by [animator]
|
||||
* @param pickerItem The Expected Text Composables in [textPickerContent].
|
||||
* text is the given Text for the Item.
|
||||
* translation is the movement of the element:
|
||||
* - 1.0 -> selected
|
||||
* - 0.5 -> unselected
|
||||
* - 0.0 -> completely outside of boundaries.
|
||||
* - movement between translation are expected to be proper (such as setting alpha based on the translation linearly).
|
||||
*/
|
||||
@Composable
|
||||
fun TextPicker(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -40,7 +75,12 @@ fun TextPicker(
|
|||
) {
|
||||
|
||||
val moveUnsafeToProperIndex: (Int) -> Int = rememberDefaultMoveUnsafeToProperIndex(itemCount = itemCount, roundAround = roundAround)
|
||||
val rememberTextForIndex = rememberWrappedTextForIndex(itemCount = itemCount, roundAround = roundAround, textForIndex = textForIndex)
|
||||
val rememberTextForIndex = rememberSafeTextForIndex(
|
||||
itemCount = itemCount,
|
||||
roundAround = roundAround,
|
||||
textForIndex = textForIndex,
|
||||
moveUnsafeToProperIndex = moveUnsafeToProperIndex
|
||||
)
|
||||
|
||||
Layout(
|
||||
modifier = modifier
|
||||
|
|
@ -70,7 +110,6 @@ fun TextPicker(
|
|||
textPickerContent.Content(
|
||||
textForIndex = rememberTextForIndex,
|
||||
item = pickerItem,
|
||||
moveUnsafeToProperIndex = moveUnsafeToProperIndex,
|
||||
selected = moveUnsafeToProperIndex(selectedIndex + state.indexOffset),
|
||||
before1TranslatePercent = state.before1TranslatePercent,
|
||||
itemTranslatePercent = state.itemTranslatePercent,
|
||||
|
|
@ -83,16 +122,3 @@ fun TextPicker(
|
|||
)
|
||||
}
|
||||
|
||||
fun interface TextPickerContent {
|
||||
@Composable
|
||||
fun Content(
|
||||
textForIndex: (Int) -> String,
|
||||
selected: Int,
|
||||
moveUnsafeToProperIndex: (Int) -> Int,
|
||||
before1TranslatePercent: Float,
|
||||
itemTranslatePercent: Float,
|
||||
after1TranslatePercent: Float,
|
||||
after2TranslatePercent: Float,
|
||||
item: @Composable (text: String, translation: Float) -> Unit
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,21 @@ import org.fknives.android.compose.picker.text.util.TextPickerDefaults
|
|||
import kotlin.math.abs
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Animator for [TextPicker]
|
||||
*
|
||||
* Handles 3 actions:
|
||||
* - Continuous Dragging via [onDeltaY]
|
||||
* - Scrolling Fling Animation via [flingAndSnap]
|
||||
* - Scrolling newly selected item via [snapToIndex]
|
||||
*
|
||||
* @param flingDecaySpec AnimationSpec calculating expected scrolling by the given velocity. Used for the Fling Animation.
|
||||
* @param flingAnimationSpec AnimationSpec used to do the actual scrolling animation, used in both animations.
|
||||
* @param velocityMultiplier Controls Velocity to Slow or Fasten fling calculation, Raw Velocity is multiplied with this parameter.
|
||||
* @param offsetLimiter Limits the offset animation. This can be used, to not let over scrolling happen after a given index or similar.
|
||||
* @param onIndexDifferenceChanging Index Difference updates as animations and dragging happens.
|
||||
* Negative values mean the index were decreased, Positive means it was increased.
|
||||
*/
|
||||
class TextPickerAnimator(
|
||||
private val flingDecaySpec: DecayAnimationSpec<Float>,
|
||||
private val flingAnimationSpec: AnimationSpec<Float> = spring(0.75f),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
|
||||
/**
|
||||
* Internal State for [TextPicker]
|
||||
*
|
||||
* Holds the Scrolling [offset] changes and Text [itemSize]-s.
|
||||
* From this the Translations and index changes are Derived.
|
||||
*/
|
||||
class TextPickerState(
|
||||
itemSizeState: MutableState<Float>,
|
||||
previousSelectedState: MutableState<Int>,
|
||||
|
|
|
|||
|
|
@ -1,24 +1,33 @@
|
|||
package org.fknives.android.compose.picker.text.content
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import org.fknives.android.compose.picker.text.TextPickerContent
|
||||
|
||||
/**
|
||||
* Default [TextPickerContent] for [TextPicker][org.fknives.android.compose.picker.text.TextPicker].
|
||||
*/
|
||||
class DefaultTextPickerContent : TextPickerContent {
|
||||
|
||||
/**
|
||||
* Creates the actual content.
|
||||
*
|
||||
* Calculates the indexes around [selected], gets each item's text via [textForIndex].
|
||||
* From that the [item]s are placed with their respective text and given translation.
|
||||
*
|
||||
* The items are expected to be places by [DefaultTextPickerContent]
|
||||
*/
|
||||
@Composable
|
||||
override fun Content(
|
||||
textForIndex: (Int) -> String,
|
||||
selected: Int,
|
||||
moveUnsafeToProperIndex: (Int) -> Int,
|
||||
before1TranslatePercent: Float,
|
||||
itemTranslatePercent: Float,
|
||||
after1TranslatePercent: Float,
|
||||
after2TranslatePercent: Float,
|
||||
item: @Composable (text: String, translation: Float) -> Unit
|
||||
) {
|
||||
val before1 = moveUnsafeToProperIndex(selected - 1)
|
||||
val after1 = moveUnsafeToProperIndex(selected + 1)
|
||||
val after2 = moveUnsafeToProperIndex(selected + 2)
|
||||
val before1 = selected - 1
|
||||
val after1 = selected + 1
|
||||
val after2 = selected + 2
|
||||
item(
|
||||
text = textForIndex(before1),
|
||||
translation = before1TranslatePercent
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import androidx.compose.ui.draw.alpha
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import org.fknives.android.compose.picker.text.util.TextPickerDefaults
|
||||
|
||||
/**
|
||||
* Default Text Label inside a [TextPicker][org.fknives.android.compose.picker.text.TextPicker]
|
||||
*/
|
||||
@Composable
|
||||
fun DefaultNumberPickerText(
|
||||
text: String,
|
||||
|
|
@ -19,6 +22,28 @@ fun DefaultNumberPickerText(
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps [numberPickerLabel], to calculate the corresponding alpha from the given Translation.
|
||||
*
|
||||
* @param unselectedAlpha The alpha value an unselected element in [TextPicker][org.fknives.android.compose.picker.text.TextPicker] should have.
|
||||
* @param selectedAlpha The alpha value a selected element in [TextPicker][org.fknives.android.compose.picker.text.TextPicker] should have.
|
||||
* @param numberPickerLabel the actual label Composable.
|
||||
* @param modifier additional [Modifier] for [numberPickerLabel]
|
||||
*
|
||||
* As described in [TextPicker][org.fknives.android.compose.picker.text.TextPicker].
|
||||
* translation is the movement of the element:
|
||||
* - 1 -> selected
|
||||
* - 0.5 -> unselected
|
||||
* - 0 -> completely outside of boundaries.
|
||||
* - movement between translation are expected to be proper (such as setting alpha based on the translation linearly).
|
||||
*
|
||||
* This mediator, calculates the linear alpha in the following way:
|
||||
* - 1.0 -> [selectedAlpha] is shown
|
||||
* - 0.75 -> middle way linearly between [selectedAlpha] and [unselectedAlpha]
|
||||
* - 0.5f -> [unselectedAlpha]
|
||||
* - 0.25 -> middle way linearly between [unselectedAlpha] and 0 Alpha.
|
||||
* - 0.0 -> completely gone 0 Alpha.
|
||||
*/
|
||||
fun defaultNumberPickerTextAlphaModifier(
|
||||
modifier: Modifier = Modifier,
|
||||
unselectedAlpha: Float = TextPickerDefaults.unselectedAlpha,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,16 @@ import androidx.compose.ui.layout.Placeable
|
|||
import androidx.compose.ui.unit.Constraints
|
||||
import org.fknives.android.compose.picker.text.TextPickerState
|
||||
|
||||
/**
|
||||
* Default TextMeasurePolicy of [TextPicker][org.fknives.android.compose.picker.text.TextPicker].
|
||||
*
|
||||
* Intended to be used together with [TextPickerContent] and [DefaultTextPickerContent].
|
||||
*
|
||||
* The expected [numberOfItemsToPlace] items are places one below the other, translated by [state].
|
||||
* Only [numberOfHeightMeasuringItems] items are counted in height, because elements are expected to be translated in and out of boundaries.
|
||||
*
|
||||
* Can be extended via [placementAfterItems] to place additional Items if necessary.
|
||||
*/
|
||||
open class DefaultTextPickerMeasurePolicy(private val state: TextPickerState) : MeasurePolicy {
|
||||
|
||||
open val numberOfItemsToPlace = 4
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import org.fknives.android.compose.picker.text.TextPickerContent
|
||||
import org.fknives.android.compose.picker.text.util.TextPickerDefaults
|
||||
import org.fknives.android.compose.picker.text.TextPickerState
|
||||
|
||||
/**
|
||||
* Default [TextPickerContent] for [LinedTextPicker][org.fknives.android.compose.picker.text.LinedTextPicker].
|
||||
*/
|
||||
class LinedTextPickerContent(
|
||||
private val dividerModifier: Modifier = Modifier,
|
||||
private val dividerColor: @Composable () -> Color = { MaterialTheme.colors.onSurface.copy(alpha = TextPickerDefaults.dividerAlpha) },
|
||||
|
|
@ -18,20 +20,28 @@ class LinedTextPickerContent(
|
|||
private val dividerStartIndent: Dp = TextPickerDefaults.dividerStartIndent
|
||||
) : TextPickerContent {
|
||||
|
||||
/**
|
||||
* Creates the actual content.
|
||||
*
|
||||
* Calculates the indexes around [selected], gets each item's text via [textForIndex].
|
||||
* From that the [item]s are placed with their respective text and given translation.
|
||||
*
|
||||
* Also adds 2 Divider configured by constructor at the end.
|
||||
* The Dividers are expected to be places by [LinedTextPickerMeasurePolicy].
|
||||
*/
|
||||
@Composable
|
||||
override fun Content(
|
||||
textForIndex: (Int) -> String,
|
||||
selected: Int,
|
||||
moveUnsafeToProperIndex: (Int) -> Int,
|
||||
before1TranslatePercent: Float,
|
||||
itemTranslatePercent: Float,
|
||||
after1TranslatePercent: Float,
|
||||
after2TranslatePercent: Float,
|
||||
item: @Composable (text: String, translation: Float) -> Unit
|
||||
) {
|
||||
val before1 = moveUnsafeToProperIndex(selected - 1)
|
||||
val after1 = moveUnsafeToProperIndex(selected + 1)
|
||||
val after2 = moveUnsafeToProperIndex(selected + 2)
|
||||
val before1 = selected - 1
|
||||
val after1 = selected + 1
|
||||
val after2 = selected + 2
|
||||
item(
|
||||
text = textForIndex(before1),
|
||||
translation = before1TranslatePercent
|
||||
|
|
@ -63,6 +73,12 @@ class LinedTextPickerContent(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as [DefaultTextPickerMeasurePolicy], with additiona Divider positioning.
|
||||
*
|
||||
* Expected to be used together with [LinedTextPickerContent] providing the Dividers,
|
||||
* which this MeasurePolicy places around the selected Item's usual place.
|
||||
*/
|
||||
class LinedTextPickerMeasurePolicy(state: TextPickerState) : DefaultTextPickerMeasurePolicy(state = state) {
|
||||
|
||||
override fun Placeable.PlacementScope.placementAfterItems(placeables: List<Placeable>, state: TextPickerState) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package org.fknives.android.compose.picker.text.content
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
fun interface TextPickerContent {
|
||||
@Composable
|
||||
fun Content(
|
||||
textForIndex: (Int) -> String,
|
||||
selected: Int,
|
||||
before1TranslatePercent: Float,
|
||||
itemTranslatePercent: Float,
|
||||
after1TranslatePercent: Float,
|
||||
after2TranslatePercent: Float,
|
||||
item: @Composable (text: String, translation: Float) -> Unit
|
||||
)
|
||||
}
|
||||
|
|
@ -3,10 +3,56 @@ package org.fknives.android.compose.picker.text.util
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
|
||||
/**
|
||||
* Remembers the lapCounterByIndexOfDifference function with fixed [selectedIndex] and [itemCount].
|
||||
*/
|
||||
@Composable
|
||||
fun rememberLapsCounterByOnIndexDifference(selectedIndex: Int, itemCount: Int) = remember(selectedIndex, itemCount) {
|
||||
createLapsCounterByOnIndexDifference(selectedIndex = selectedIndex, itemCount = itemCount)
|
||||
}
|
||||
|
||||
/**
|
||||
* Curry function for [lapCounterByIndexOfDifference]
|
||||
* Keeps [itemCount] and [selectedIndex] for scope.
|
||||
*
|
||||
* @return Returned lambda expectes the indexDifference for the [lapCounterByIndexOfDifference] calculation.
|
||||
*/
|
||||
fun createLapsCounterByOnIndexDifference(itemCount: Int, selectedIndex: Int): (Int) -> Int = { indexDifference ->
|
||||
lapCounterByIndexOfDifference(itemCount = itemCount, indexDifference = indexDifference, selectedIndex = selectedIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function intended to be used with onIndexDifferenceChanging of [TextPicker][org.fknives.android.compose.picker.text.TextPicker].
|
||||
*
|
||||
* Calculates how many times the TextPicker were scrolled over (first element -to-> last element by index decrease or vice versa)
|
||||
* Useful when OverScrolling needs some reaction.
|
||||
*
|
||||
* @param itemCount Number of items
|
||||
* @param selectedIndex the currently selected index, must be between `0 until [itemCount]`
|
||||
* @param indexDifference the current index difference from [selectedIndex].
|
||||
*
|
||||
* @return The Round Around Scrolling.
|
||||
*
|
||||
* Example1:
|
||||
* `itemCount = 3, selectedIndex = 0, indexDifference = -1.`
|
||||
* This results in -1, meaning the currently shown element is index 2. 1 overscroll happened from index 0 to index 2.
|
||||
*
|
||||
* Example2:
|
||||
* `itemCount = 3, selectedIndex = 0, indexDifference = -3.`
|
||||
* This results in -1, meaning the currently shown element is index 0. 1 overscroll happened from index 0 to index 2.
|
||||
*
|
||||
* Example3:
|
||||
* `itemCount = 3, selectedIndex = 0, indexDifference = -4.`
|
||||
* This results in -2, meaning the currently shown element is index 2. 2 overscroll happened from index 0 to index 2.
|
||||
*
|
||||
* Example3:
|
||||
* `itemCount = 3, selectedIndex = 0, indexDifference = 3.`
|
||||
* This results in 1, meaning the currently shown element is index 0. 1 overscroll happened from index 2 to index 0.
|
||||
*
|
||||
* Example3:
|
||||
* `itemCount = 3, selectedIndex = 0, indexDifference = 5.`
|
||||
* This results in 1, meaning the currently shown element is index 2. 1 overscroll happened from index 2 to index 0.
|
||||
*/
|
||||
fun lapCounterByIndexOfDifference(
|
||||
itemCount: Int,
|
||||
selectedIndex: Int,
|
||||
|
|
@ -28,9 +74,4 @@ fun lapCounterByIndexOfDifference(
|
|||
}
|
||||
|
||||
return counter
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberLapsCounterByOnIndexDifference(selectedIndex: Int, itemCount: Int) = remember(selectedIndex, itemCount) {
|
||||
createLapsCounterByOnIndexDifference(selectedIndex = selectedIndex, itemCount = itemCount)
|
||||
}
|
||||
|
|
@ -4,10 +4,24 @@ import org.fknives.android.compose.picker.text.TextPickerState
|
|||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Scrolling Offset Limiter intended for [TextPickerAnimator][org.fknives.android.compose.picker.text.TextPickerAnimator].
|
||||
*
|
||||
* Provides to [default]s:
|
||||
* - [NoLimit] Used when scrolling should not be limited
|
||||
* (like RoundAround ([TextPicker][org.fknives.android.compose.picker.text.TextPicker])
|
||||
* - [MinMaxLimit] Used when scrolling should be limited to maximum scroll of up to First Element and down to LastElement.
|
||||
*/
|
||||
fun interface OffsetLimiter {
|
||||
|
||||
/**
|
||||
* Hard limit on offset, to ensure indexes are within range.
|
||||
*/
|
||||
fun limit(offset: Float, state: TextPickerState): Float
|
||||
|
||||
/**
|
||||
* Soft limit on offset, to enable overshooting for animations.
|
||||
*/
|
||||
fun overshootLimit(offset: Float, state: TextPickerState): Float =
|
||||
limit(offset, state)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,19 @@ package org.fknives.android.compose.picker.text.util
|
|||
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* Default values for [TextPicker][org.fknives.android.compose.picker.text.TextPicker]
|
||||
*/
|
||||
object TextPickerDefaults {
|
||||
const val selected = 0
|
||||
const val velocityMultiplier = 0.3f
|
||||
const val unselectedAlpha = 0.6f
|
||||
const val selectedAlpha = 1f
|
||||
/**
|
||||
* [TextPickerAnimator][org.fknives.android.compose.picker.text.TextPickerAnimator] checks against this to not emit unnecessary updates.
|
||||
*/
|
||||
internal val onIndexDifferenceChanging: (Int) -> Unit = {}
|
||||
|
||||
const val roundAround = true
|
||||
const val dividerAlpha = 0.12f
|
||||
val dividerThickness = 2.dp
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import androidx.compose.ui.unit.Density
|
|||
import org.fknives.android.compose.picker.text.TextPickerAnimator
|
||||
import org.fknives.android.compose.picker.text.TextPickerState
|
||||
|
||||
/**
|
||||
* Helper function caching [TextPickerState]
|
||||
*/
|
||||
@Composable
|
||||
fun rememberTextPickerState(selected: Int, itemCount: Int): TextPickerState {
|
||||
require(selected >= 0) { "Selected value ($selected) is less than 0!" }
|
||||
|
|
@ -26,6 +29,9 @@ fun rememberTextPickerState(selected: Int, itemCount: Int): TextPickerState {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function caching [TextPickerAnimator]
|
||||
*/
|
||||
@Composable
|
||||
fun rememberTextPickerAnimator(
|
||||
roundAround: Boolean,
|
||||
|
|
@ -43,6 +49,9 @@ fun rememberTextPickerAnimator(
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function caching [TextPickerAnimator]
|
||||
*/
|
||||
@Composable
|
||||
fun rememberTextPickerAnimator(
|
||||
offsetLimiter: OffsetLimiter,
|
||||
|
|
@ -61,6 +70,12 @@ fun rememberTextPickerAnimator(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Lambda changing index based on [roundAround].
|
||||
*
|
||||
* If roundAround is False, means the index is not touched, since it should not behave like a wheel.
|
||||
* IF roundAround is True, means the index is feed into [moveUnsafeToProperIndex] to behave like a wheel.
|
||||
*/
|
||||
@Composable
|
||||
fun rememberDefaultMoveUnsafeToProperIndex(itemCount: Int, roundAround: Boolean) =
|
||||
if (roundAround) {
|
||||
|
|
@ -69,12 +84,21 @@ fun rememberDefaultMoveUnsafeToProperIndex(itemCount: Int, roundAround: Boolean)
|
|||
remember { { index: Int -> index } }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Curry function for [moveUnsafeToProperIndex] which stores the [itemCount]
|
||||
*
|
||||
* @return lambda expecting the unsafeIndex to call [moveUnsafeToProperIndex]
|
||||
*/
|
||||
fun createMoveUnsafeToProperIndex(itemCount: Int) = moveUnsafeToProperIndex@{ unsafeIndex: Int ->
|
||||
moveUnsafeToProperIndex(unsafeIndex, itemCount)
|
||||
}
|
||||
|
||||
fun moveUnsafeToProperIndex(unsafeIndex: Int, itemCount: Int) : Int {
|
||||
/**
|
||||
* Moves a given [unsafeIndex] between `0 until [itemCount]` like it is a continuous wheel.
|
||||
* Meaning that `unsafeIndex=-1` becomes `itemCount-1` while `unsafeIndex=itemCount` becomes 0.
|
||||
*
|
||||
*/
|
||||
fun moveUnsafeToProperIndex(unsafeIndex: Int, itemCount: Int): Int {
|
||||
var index = unsafeIndex
|
||||
while (index < 0) {
|
||||
index += itemCount
|
||||
|
|
@ -85,13 +109,42 @@ fun moveUnsafeToProperIndex(unsafeIndex: Int, itemCount: Int) : Int {
|
|||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches function accessing text for given index safely.
|
||||
* [roundAround] True means the Index will be modified if it needs to be to fall between the safe range for [textForIndex]
|
||||
* [roundAround] False means the if the Index is falls outside of safeZone, [textForUnsafeIndex] will be used as fallback.
|
||||
* Note: unsafeIndex means the index NOT between `0 until itemCount`.
|
||||
*/
|
||||
@Composable
|
||||
fun rememberWrappedTextForIndex(itemCount: Int, roundAround: Boolean, textForIndex: (Int) -> String): (Int) -> String =
|
||||
fun rememberSafeTextForIndex(
|
||||
textForUnsafeIndex: String = "",
|
||||
itemCount: Int,
|
||||
roundAround: Boolean,
|
||||
textForIndex: (Int) -> String,
|
||||
moveUnsafeToProperIndex: (Int) -> Int
|
||||
): (Int) -> String =
|
||||
if (roundAround) {
|
||||
textForIndex
|
||||
remember(moveUnsafeToProperIndex, textForIndex) {
|
||||
createSafeRoundAroundTextForIndex(moveUnsafeToProperIndex = moveUnsafeToProperIndex, textForIndex = textForIndex)
|
||||
}
|
||||
} else {
|
||||
remember(itemCount, textForIndex) {
|
||||
{ index -> if (index < 0 || index >= itemCount) "" else textForIndex(index) }
|
||||
createSafeOrDefaultTextForIndex(itemCount = itemCount, textForIndex = textForIndex, textForUnsafeIndex = textForUnsafeIndex)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns lambda, which ensures the index is moved to into safe position by [moveUnsafeToProperIndex] before accessing [textForIndex]
|
||||
*/
|
||||
fun createSafeRoundAroundTextForIndex(moveUnsafeToProperIndex: (Int) -> Int, textForIndex: (Int) -> String): (Int) -> String =
|
||||
{ index: Int ->
|
||||
textForIndex(moveUnsafeToProperIndex(index))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns lambda, which ensures the index is between `0 until itemCount` before accessing [textForIndex], fallbacks [textForUnsafeIndex]
|
||||
*/
|
||||
fun createSafeOrDefaultTextForIndex(textForUnsafeIndex: String = "", itemCount: Int, textForIndex: (Int) -> String): (Int) -> String =
|
||||
{ index: Int ->
|
||||
if (index in 0 until itemCount) textForIndex(index) else textForUnsafeIndex
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue