Добавлен clickableDebounced

This commit is contained in:
Пытков Роман
2025-02-05 13:27:20 +03:00
parent f7071382a7
commit 2cb2dabe3f
4 changed files with 117 additions and 35 deletions

View File

@@ -38,6 +38,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.github.nullptroma.wallenc.domain.datatypes.Tree import com.github.nullptroma.wallenc.domain.datatypes.Tree
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
import com.github.nullptroma.wallenc.presentation.R import com.github.nullptroma.wallenc.presentation.R
import com.github.nullptroma.wallenc.presentation.elements.indication.ScaleIndication
import com.github.nullptroma.wallenc.presentation.extensions.clickableDebounced
@Composable @Composable
fun StorageTree( fun StorageTree(
@@ -54,9 +56,8 @@ fun StorageTree(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clip(cardShape) .clip(cardShape)
.clickable { .clickableDebounced(debounceMs = 500) {
onClick(tree) onClick(tree)
//viewModel.printStorageInfoToLog(cur)
}, },
shape = cardShape, shape = cardShape,
elevation = CardDefaults.cardElevation( elevation = CardDefaults.cardElevation(
@@ -134,7 +135,10 @@ fun StorageTree(
showRemoveConfirmationDiaglog = false showRemoveConfirmationDiaglog = false
onRemove(tree) onRemove(tree)
}, },
title = stringResource(R.string.remove_confirmation_dialog, metaInfo.name ?: "<noname>") title = stringResource(
R.string.remove_confirmation_dialog,
metaInfo.name ?: "<noname>"
)
) )
} }
} }

View File

@@ -0,0 +1,61 @@
package com.github.nullptroma.wallenc.presentation.elements.indication
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.spring
import androidx.compose.foundation.IndicationNodeFactory
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.node.DelegatableNode
import androidx.compose.ui.node.DrawModifierNode
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
private class ScaleNode(private val interactionSource: InteractionSource) :
Modifier.Node(), DrawModifierNode {
var currentPressPosition: Offset = Offset.Zero
val animatedScalePercent = Animatable(1f)
private suspend fun animateToPressed(pressPosition: Offset) {
currentPressPosition = pressPosition
animatedScalePercent.animateTo(0.9f, spring())
}
private suspend fun animateToResting() {
animatedScalePercent.animateTo(1f, spring())
}
override fun onAttach() {
coroutineScope.launch {
interactionSource.interactions.collectLatest { interaction ->
when (interaction) {
is PressInteraction.Press -> animateToPressed(interaction.pressPosition)
is PressInteraction.Release -> animateToResting()
is PressInteraction.Cancel -> animateToResting()
}
}
}
}
override fun ContentDrawScope.draw() {
scale(
scale = animatedScalePercent.value,
pivot = currentPressPosition
) {
this@draw.drawContent()
}
}
}
object ScaleIndication : IndicationNodeFactory {
override fun create(interactionSource: InteractionSource): DelegatableNode {
return ScaleNode(interactionSource)
}
override fun equals(other: Any?): Boolean = other === ScaleIndication
override fun hashCode() = 100
}

View File

@@ -1,7 +1,16 @@
package com.github.nullptroma.wallenc.presentation.extensions package com.github.nullptroma.wallenc.presentation.extensions
import androidx.compose.foundation.Indication
import androidx.compose.foundation.IndicationNodeFactory
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
fun Modifier.ignoreHorizontalParentPadding(horizontal: Dp): Modifier { fun Modifier.ignoreHorizontalParentPadding(horizontal: Dp): Modifier {
@@ -23,3 +32,43 @@ fun Modifier.ignoreVerticalParentPadding(vertical: Dp): Modifier {
} }
} }
} }
fun Modifier.clickableDebounced(
interactionSource: MutableInteractionSource? = null,
indication: Indication? = null,
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
debounceMs: Long = 300,
onClick: () -> Unit
): Modifier = composed(
inspectorInfo = debugInspectorInfo {
name = "clickableDebounced"
properties["enabled"] = enabled
properties["onClickLabel"] = onClickLabel
properties["role"] = role
properties["onClick"] = onClick
}
) {
var latest: Long = 0
val ind = indication ?: LocalIndication.current
val interSource = if (ind is IndicationNodeFactory) {
null
} else {
interactionSource ?: remember { MutableInteractionSource() }
}
return@composed this@clickableDebounced.clickable(
interactionSource = interSource,
indication = ind,
enabled = enabled,
onClickLabel = onClickLabel,
role = role,
onClick = {
val now = System.currentTimeMillis()
if (now - latest >= debounceMs) {
latest = now
onClick()
}
}
)
}

View File

@@ -1,54 +1,22 @@
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CardElevation
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.github.nullptroma.wallenc.domain.datatypes.Tree
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
import com.github.nullptroma.wallenc.presentation.R
import com.github.nullptroma.wallenc.presentation.elements.StorageTree import com.github.nullptroma.wallenc.presentation.elements.StorageTree
import kotlin.random.Random
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable