Skip to content

Commit 4858167

Browse files
committed
Added clarifying comments to code and used better variable names
1 parent bda31e9 commit 4858167

File tree

4 files changed

+75
-67
lines changed

4 files changed

+75
-67
lines changed

core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/AppScrollbars.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollba
4949
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.ThumbState.Inactive
5050
import kotlinx.coroutines.delay
5151

52+
private const val INACTIVE_TO_DORMANT_COOL_DOWN = 2_000L
53+
5254
/**
5355
* A [Scrollbar] that allows for fast scrolling of content.
5456
* Its thumb disappears when the scrolling container is dormant.
@@ -80,7 +82,7 @@ fun FastScrollbar(
8082
orientation = orientation,
8183
)
8284
},
83-
onThumbMoved = onThumbMoved,
85+
onThumbDisplaced = onThumbMoved,
8486
)
8587
}
8688

@@ -202,7 +204,7 @@ private fun scrollbarThumbColor(
202204
true -> state = Active
203205
false -> {
204206
state = Inactive
205-
delay(2_000)
207+
delay(INACTIVE_TO_DORMANT_COOL_DOWN)
206208
state = Dormant
207209
}
208210
}

core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import kotlin.math.min
3333
* Calculates the [ScrollbarState] for lazy layouts.
3434
* @param itemsAvailable the total amount of items available to scroll in the layout.
3535
* @param visibleItems a list of items currently visible in the layout.
36-
* @param firstItemIndex a function for interpolating the first visible index in the lazy layout
36+
* @param firstVisibleItemIndex a function for interpolating the first visible index in the lazy layout
3737
* as scrolling progresses for smooth and linear scrollbar thumb progression.
3838
* [itemsAvailable].
3939
* @param reverseLayout if the items in the backing lazy layout are laid out in reverse order.
@@ -42,7 +42,7 @@ import kotlin.math.min
4242
internal inline fun <LazyState : ScrollableState, LazyStateItem> LazyState.scrollbarState(
4343
itemsAvailable: Int,
4444
crossinline visibleItems: LazyState.() -> List<LazyStateItem>,
45-
crossinline firstItemIndex: LazyState.(List<LazyStateItem>) -> Float,
45+
crossinline firstVisibleItemIndex: LazyState.(List<LazyStateItem>) -> Float,
4646
crossinline itemPercentVisible: LazyState.(LazyStateItem) -> Float,
4747
crossinline reverseLayout: LazyState.() -> Boolean,
4848
): ScrollbarState {
@@ -58,9 +58,8 @@ internal inline fun <LazyState : ScrollableState, LazyStateItem> LazyState.scrol
5858
val visibleItemsInfo = visibleItems(this@scrollbarState)
5959
if (visibleItemsInfo.isEmpty()) return@snapshotFlow null
6060

61-
// Add the item offset for interpolation between scroll indices
6261
val firstIndex = min(
63-
a = firstItemIndex(visibleItemsInfo),
62+
a = firstVisibleItemIndex(visibleItemsInfo),
6463
b = itemsAvailable.toFloat(),
6564
)
6665
if (firstIndex.isNaN()) return@snapshotFlow null
@@ -77,10 +76,9 @@ internal inline fun <LazyState : ScrollableState, LazyStateItem> LazyState.scrol
7776
a = itemsVisible / itemsAvailable,
7877
b = 1f,
7978
)
80-
8179
ScrollbarState(
8280
thumbSizePercent = thumbSizePercent,
83-
thumbTravelPercent = when {
81+
thumbDisplacementPercent = when {
8482
reverseLayout() -> 1f - thumbTravelPercent
8583
else -> thumbTravelPercent
8684
},

core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/Scrollbar.kt

Lines changed: 65 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ import kotlinx.coroutines.delay
5757
import kotlin.math.max
5858
import kotlin.math.min
5959

60-
private const val SCROLLBAR_PRESS_DELAY = 100L
61-
private const val SCROLLBAR_PRESS_DELTA = 0.1f
60+
private const val SCROLLBAR_PRESS_DELAY = 10L
61+
private const val SCROLLBAR_PRESS_DELTA = 0.02f
6262

6363
/**
6464
* Class definition for the core properties of a scroll bar
@@ -71,7 +71,7 @@ value class ScrollbarState internal constructor(
7171
companion object {
7272
val FULL = ScrollbarState(
7373
thumbSizePercent = 1f,
74-
thumbTravelPercent = 0f,
74+
thumbDisplacementPercent = 0f,
7575
)
7676
}
7777
}
@@ -93,15 +93,16 @@ private value class ScrollbarTrack(
9393
/**
9494
* Creates a scrollbar state with the listed properties
9595
* @param thumbSizePercent the thumb size of the scrollbar as a percentage of the total track size
96-
* @param thumbTravelPercent the distance the thumb has traveled as a percentage of total track size
96+
* @param thumbDisplacementPercent the distance the thumb has traveled as a percentage of total
97+
* track size
9798
*/
9899
fun ScrollbarState(
99100
thumbSizePercent: Float,
100-
thumbTravelPercent: Float,
101+
thumbDisplacementPercent: Float,
101102
) = ScrollbarState(
102103
packFloats(
103104
val1 = thumbSizePercent,
104-
val2 = thumbTravelPercent,
105+
val2 = thumbDisplacementPercent,
105106
),
106107
)
107108

@@ -114,7 +115,7 @@ val ScrollbarState.thumbSizePercent
114115
/**
115116
* Returns the distance the thumb has traveled as a percentage of total track size
116117
*/
117-
val ScrollbarState.thumbTravelPercent
118+
val ScrollbarState.thumbDisplacementPercent
118119
get() = unpackFloat2(packedValue)
119120

120121
/**
@@ -167,8 +168,8 @@ internal fun Orientation.valueOf(intOffset: IntOffset) = when (this) {
167168
* @param minThumbSize the minimum size of the scrollbar thumb
168169
* @param interactionSource allows for observing the state of the scroll bar
169170
* @param thumb a composable for drawing the scrollbar thumb
170-
* @param onThumbMoved an function for reacting to scroll bar interactions, for example implementing
171-
* a fast scroll
171+
* @param onThumbDisplaced an function for reacting to scroll bar displacements caused by direct
172+
* interactions on the scrollbar thumb by the user, for example implementing a fast scroll
172173
*/
173174
@Composable
174175
fun Scrollbar(
@@ -178,50 +179,47 @@ fun Scrollbar(
178179
minThumbSize: Dp = 40.dp,
179180
interactionSource: MutableInteractionSource? = null,
180181
thumb: @Composable () -> Unit,
181-
onThumbMoved: ((Float) -> Unit)? = null,
182+
onThumbDisplaced: ((Float) -> Unit)? = null,
182183
) {
183184
val localDensity = LocalDensity.current
184-
var interactionThumbTravelPercent by remember { mutableStateOf(Float.NaN) }
185+
186+
// Using Offset.Unspecified and Float.NaN instead of null
187+
// to prevent unnecessary boxing of primitives
185188
var pressedOffset by remember { mutableStateOf(Offset.Unspecified) }
186189
var draggedOffset by remember { mutableStateOf(Offset.Unspecified) }
187190

188-
var track by remember { mutableStateOf(ScrollbarTrack(0)) }
189-
val updatedState by rememberUpdatedState(state)
190-
val updatedTrack by rememberUpdatedState(track)
191+
// Used to immediately show drag feedback in the UI while the scrolling implementation
192+
// catches up
193+
var interactionThumbTravelPercent by remember { mutableStateOf(Float.NaN) }
194+
195+
var track by remember { mutableStateOf(ScrollbarTrack(packedValue = 0)) }
191196

192-
val thumbSizePercent = state.thumbSizePercent
193197
val thumbTravelPercent = when {
194-
interactionThumbTravelPercent.isNaN() -> state.thumbTravelPercent
198+
interactionThumbTravelPercent.isNaN() -> state.thumbDisplacementPercent
195199
else -> interactionThumbTravelPercent
196200
}
197201
val thumbSizePx = max(
198-
a = thumbSizePercent * track.size,
202+
a = state.thumbSizePercent * track.size,
199203
b = with(localDensity) { minThumbSize.toPx() },
200204
)
201-
202205
val thumbSizeDp by animateDpAsState(
203206
targetValue = with(localDensity) { thumbSizePx.toDp() },
204-
label = "thumb size",
207+
label = "scrollbar thumb size",
205208
)
206-
207-
val thumbTravelPx = min(
209+
val thumbDisplacementPx = min(
208210
a = track.size * thumbTravelPercent,
209211
b = track.size - thumbSizePx,
210212
)
211-
212213
val draggableState = rememberDraggableState { delta ->
213214
if (draggedOffset == Offset.Unspecified) return@rememberDraggableState
214215

215216
draggedOffset = when (orientation) {
216-
Orientation.Vertical -> draggedOffset.copy(
217-
y = draggedOffset.y + delta,
218-
)
219-
220-
Orientation.Horizontal -> draggedOffset.copy(
221-
x = draggedOffset.x + delta,
222-
)
217+
Orientation.Vertical -> draggedOffset.copy(y = draggedOffset.y + delta)
218+
Orientation.Horizontal -> draggedOffset.copy(x = draggedOffset.x + delta)
223219
}
224220
}
221+
222+
// Scrollbar track container
225223
Box(
226224
modifier = modifier
227225
.run {
@@ -232,28 +230,28 @@ fun Scrollbar(
232230
}
233231
}
234232
.onGloballyPositioned { coordinates ->
235-
val position = orientation.valueOf(coordinates.positionInRoot())
233+
val scrollbarStartCoordinate = orientation.valueOf(coordinates.positionInRoot())
236234
track = ScrollbarTrack(
237-
max = position,
238-
min = position + orientation.valueOf(coordinates.size),
235+
max = scrollbarStartCoordinate,
236+
min = scrollbarStartCoordinate + orientation.valueOf(coordinates.size),
239237
)
240238
}
241239
// Process scrollbar presses
242240
.pointerInput(Unit) {
243241
detectTapGestures(
244242
onPress = { offset ->
245243
val initialPress = PressInteraction.Press(offset)
246-
247244
interactionSource?.tryEmit(initialPress)
245+
246+
// Start the press
248247
pressedOffset = offset
249248

250249
interactionSource?.tryEmit(
251-
if (tryAwaitRelease()) {
252-
PressInteraction.Release(initialPress)
253-
} else {
254-
PressInteraction.Cancel(initialPress)
255-
},
250+
if (tryAwaitRelease()) PressInteraction.Release(initialPress)
251+
else PressInteraction.Cancel(initialPress),
256252
)
253+
254+
// End the press
257255
pressedOffset = Offset.Unspecified
258256
},
259257
)
@@ -271,10 +269,11 @@ fun Scrollbar(
271269
},
272270
),
273271
) {
274-
val offset = max(
275-
a = with(localDensity) { thumbTravelPx.toDp() },
272+
val scrollbarThumbDisplacement = max(
273+
a = with(localDensity) { thumbDisplacementPx.toDp() },
276274
b = 0.dp,
277275
)
276+
// Scrollbar thumb container
278277
Box(
279278
modifier = Modifier
280279
.align(Alignment.TopStart)
@@ -287,10 +286,10 @@ fun Scrollbar(
287286
.offset(
288287
y = when (orientation) {
289288
Orientation.Horizontal -> 0.dp
290-
Orientation.Vertical -> offset
289+
Orientation.Vertical -> scrollbarThumbDisplacement
291290
},
292291
x = when (orientation) {
293-
Orientation.Horizontal -> offset
292+
Orientation.Horizontal -> scrollbarThumbDisplacement
294293
Orientation.Vertical -> 0.dp
295294
},
296295
),
@@ -299,31 +298,40 @@ fun Scrollbar(
299298
}
300299
}
301300

302-
if (onThumbMoved == null) return
301+
if (onThumbDisplaced == null) return
302+
303+
// State that will be read inside the effects that follow
304+
// but will not cause re-triggering of them
305+
val updatedState by rememberUpdatedState(state)
303306

304307
// Process presses
305308
LaunchedEffect(pressedOffset) {
309+
// Press ended, reset interactionThumbTravelPercent
306310
if (pressedOffset == Offset.Unspecified) {
307311
interactionThumbTravelPercent = Float.NaN
308312
return@LaunchedEffect
309313
}
310314

311-
var currentTravel = updatedState.thumbTravelPercent
312-
val destinationTravel = updatedTrack.thumbPosition(
315+
var currentThumbDisplacement = updatedState.thumbDisplacementPercent
316+
val destinationThumbDisplacement = track.thumbPosition(
313317
dimension = orientation.valueOf(pressedOffset),
314318
)
315-
val isPositive = currentTravel < destinationTravel
319+
val isPositive = currentThumbDisplacement < destinationThumbDisplacement
316320
val delta = SCROLLBAR_PRESS_DELTA * if (isPositive) 1f else -1f
317321

318-
while (currentTravel != destinationTravel) {
319-
currentTravel =
320-
if (isPositive) {
321-
min(currentTravel + delta, destinationTravel)
322-
} else {
323-
max(currentTravel + delta, destinationTravel)
324-
}
325-
onThumbMoved(currentTravel)
326-
interactionThumbTravelPercent = currentTravel
322+
while (currentThumbDisplacement != destinationThumbDisplacement) {
323+
currentThumbDisplacement = when {
324+
isPositive -> min(
325+
a = currentThumbDisplacement + delta,
326+
b = destinationThumbDisplacement,
327+
)
328+
else -> max(
329+
a = currentThumbDisplacement + delta,
330+
b = destinationThumbDisplacement,
331+
)
332+
}
333+
onThumbDisplaced(currentThumbDisplacement)
334+
interactionThumbTravelPercent = currentThumbDisplacement
327335
delay(SCROLLBAR_PRESS_DELAY)
328336
}
329337
}
@@ -334,10 +342,10 @@ fun Scrollbar(
334342
interactionThumbTravelPercent = Float.NaN
335343
return@LaunchedEffect
336344
}
337-
val currentTravel = updatedTrack.thumbPosition(
345+
val currentTravel = track.thumbPosition(
338346
dimension = orientation.valueOf(draggedOffset),
339347
)
340-
onThumbMoved(currentTravel)
348+
onThumbDisplaced(currentTravel)
341349
interactionThumbTravelPercent = currentTravel
342350
}
343351
}

core/designsystem/src/main/java/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fun LazyListState.scrollbarState(
3737
scrollbarState(
3838
itemsAvailable = itemsAvailable,
3939
visibleItems = { layoutInfo.visibleItemsInfo },
40-
firstItemIndex = { visibleItems ->
40+
firstVisibleItemIndex = { visibleItems ->
4141
interpolateFirstItemIndex(
4242
visibleItems = visibleItems,
4343
itemSize = { it.size },
@@ -71,7 +71,7 @@ fun LazyGridState.scrollbarState(
7171
scrollbarState(
7272
itemsAvailable = itemsAvailable,
7373
visibleItems = { layoutInfo.visibleItemsInfo },
74-
firstItemIndex = { visibleItems ->
74+
firstVisibleItemIndex = { visibleItems ->
7575
interpolateFirstItemIndex(
7676
visibleItems = visibleItems,
7777
itemSize = {

0 commit comments

Comments
 (0)