PullRefreshIndicatorTransform.kt (3090B) - raw


      1 /*
      2  * Copyright 2022 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 @file:Suppress("DEPRECATION")
     18 
     19 package me.rhunk.snapenhance.ui.util.pullrefresh
     20 
     21 import androidx.compose.animation.core.LinearOutSlowInEasing
     22 import androidx.compose.ui.Modifier
     23 import androidx.compose.ui.draw.drawWithContent
     24 import androidx.compose.ui.graphics.drawscope.clipRect
     25 import androidx.compose.ui.graphics.graphicsLayer
     26 import androidx.compose.ui.platform.debugInspectorInfo
     27 import androidx.compose.ui.platform.inspectable
     28 
     29 /**
     30  * A modifier for translating the position and scaling the size of a pull-to-refresh indicator
     31  * based on the given [PullRefreshState].
     32  *
     33  * @sample androidx.compose.material.samples.PullRefreshIndicatorTransformSample
     34  *
     35  * @param state The [PullRefreshState] which determines the position of the indicator.
     36  * @param scale A boolean controlling whether the indicator's size scales with pull progress or not.
     37  */
     38 // TODO: Consider whether the state parameter should be replaced with lambdas.
     39 fun Modifier.pullRefreshIndicatorTransform(
     40     state: PullRefreshState,
     41     scale: Boolean = false,
     42 ) = inspectable(
     43     inspectorInfo = debugInspectorInfo {
     44         name = "pullRefreshIndicatorTransform"
     45         properties["state"] = state
     46         properties["scale"] = scale
     47     },
     48 ) {
     49     Modifier
     50         // Essentially we only want to clip the at the top, so the indicator will not appear when
     51         // the position is 0. It is preferable to clip the indicator as opposed to the layout that
     52         // contains the indicator, as this would also end up clipping shadows drawn by items in a
     53         // list for example - so we leave the clipping to the scrolling container. We use MAX_VALUE
     54         // for the other dimensions to allow for more room for elevation / arbitrary indicators - we
     55         // only ever really want to clip at the top edge.
     56         .drawWithContent {
     57             clipRect(
     58                 top = 0f,
     59                 left = -Float.MAX_VALUE,
     60                 right = Float.MAX_VALUE,
     61                 bottom = Float.MAX_VALUE,
     62             ) {
     63                 this@drawWithContent.drawContent()
     64             }
     65         }
     66         .graphicsLayer {
     67             translationY = state.position - size.height
     68 
     69             if (scale && !state.refreshing) {
     70                 val scaleFraction = LinearOutSlowInEasing
     71                     .transform(state.position / state.threshold)
     72                     .coerceIn(0f, 1f)
     73                 scaleX = scaleFraction
     74                 scaleY = scaleFraction
     75             }
     76         }
     77 }