From 2d3ab823a13f9ddda93d5ad5d5a7c61250aba7b1 Mon Sep 17 00:00:00 2001 From: T8RIN Date: Sun, 5 May 2024 02:06:39 +0300 Subject: [PATCH] focus clearing on tapping outside text fields --- .../core/ui/widget/buttons/EnhancedButton.kt | 5 ++++ .../buttons/EnhancedFloatingActionButton.kt | 5 ++++ .../ui/widget/buttons/ToggleGroupButton.kt | 3 +++ .../core/ui/widget/controls/EnhancedSlider.kt | 8 +++++++ .../core/ui/widget/controls/EnhancedSwitch.kt | 3 +++ .../core/ui/widget/other/LoadingDialog.kt | 18 ++++++++++++-- .../feature/crop/presentation/CropScreen.kt | 24 +++++++++++++++++-- .../presentation/components/CropEditOption.kt | 14 ++++++++++- 8 files changed, 75 insertions(+), 5 deletions(-) diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/EnhancedButton.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/EnhancedButton.kt index 0879a0630..2f545d17f 100644 --- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/EnhancedButton.kt +++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/EnhancedButton.kt @@ -41,6 +41,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -78,12 +79,14 @@ fun EnhancedButton( ) { val settingsState = LocalSettingsState.current val haptics = LocalHapticFeedback.current + val focus = LocalFocusManager.current LocalMinimumInteractiveComponentSize.ProvidesValue(Dp.Unspecified) { Box { OutlinedButton( onClick = { onClick() + focus.clearFocus() haptics.performHapticFeedback( HapticFeedbackType.LongPress ) @@ -135,11 +138,13 @@ fun EnhancedIconButton( ) { val settingsState = LocalSettingsState.current val haptics = LocalHapticFeedback.current + val focus = LocalFocusManager.current LocalMinimumInteractiveComponentSize.ProvidesValue(Dp.Unspecified) { OutlinedIconButton( onClick = { onClick() + focus.clearFocus() haptics.performHapticFeedback( HapticFeedbackType.LongPress ) diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/EnhancedFloatingActionButton.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/EnhancedFloatingActionButton.kt index 6b30baaa5..9fef65661 100644 --- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/EnhancedFloatingActionButton.kt +++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/EnhancedFloatingActionButton.kt @@ -39,6 +39,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.unit.Dp @@ -66,6 +67,7 @@ fun EnhancedFloatingActionButton( val settingsState = LocalSettingsState.current val size by animateDpAsState(type.size) val haptics = LocalHapticFeedback.current + val focus = LocalFocusManager.current LocalMinimumInteractiveComponentSize.ProvidesValue(Dp.Unspecified) { if (onLongClick != null) { @@ -82,6 +84,7 @@ fun EnhancedFloatingActionButton( delay(viewConfiguration.longPressTimeoutMillis) isLongClick = true onLongClick() + focus.clearFocus() haptics.performHapticFeedback( HapticFeedbackType.LongPress ) @@ -90,6 +93,7 @@ fun EnhancedFloatingActionButton( is PressInteraction.Release -> { if (!isLongClick) { onClick() + focus.clearFocus() haptics.performHapticFeedback( HapticFeedbackType.TextHandleMove ) @@ -108,6 +112,7 @@ fun EnhancedFloatingActionButton( onClick = { if (onLongClick == null) { onClick() + focus.clearFocus() haptics.performHapticFeedback( HapticFeedbackType.LongPress ) diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/ToggleGroupButton.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/ToggleGroupButton.kt index 54a24b99c..9e3f7eb4b 100644 --- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/ToggleGroupButton.kt +++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/buttons/ToggleGroupButton.kt @@ -44,6 +44,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -180,10 +181,12 @@ fun ToggleGroupButton( MaterialTheme.colorScheme.surfaceContainer } val selected = index == selectedIndex + val focus = LocalFocusManager.current SegmentedButton( enabled = enabled, onClick = { + focus.clearFocus() haptics.performHapticFeedback( HapticFeedbackType.LongPress ) diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/EnhancedSlider.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/EnhancedSlider.kt index 114d86a1d..a594fc0a9 100644 --- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/EnhancedSlider.kt +++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/EnhancedSlider.kt @@ -47,6 +47,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -102,6 +103,13 @@ fun EnhancedSlider( val thumb: @Composable (SliderState) -> Unit = { val interaction by interactionSource.interactions.collectAsState(initial = null) + val focus = LocalFocusManager.current + + LaunchedEffect(interaction) { + if (interaction is PressInteraction.Press) { + focus.clearFocus() + } + } val elevation = if (interaction is PressInteraction.Press) { 6.dp diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/EnhancedSwitch.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/EnhancedSwitch.kt index 7018f1d98..8522f0d54 100644 --- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/EnhancedSwitch.kt +++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/controls/EnhancedSwitch.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -70,6 +71,7 @@ fun EnhancedSwitch( ) val settingsState = LocalSettingsState.current val haptics = LocalHapticFeedback.current + val focus = LocalFocusManager.current LocalMinimumInteractiveComponentSize.ProvidesValue(Dp.Unspecified) { val switchModifier = modifier @@ -92,6 +94,7 @@ fun EnhancedSwitch( haptics.performHapticFeedback( HapticFeedbackType.LongPress ) + focus.clearFocus() } } val thumbContent: (@Composable () -> Unit)? = thumbIcon?.let { diff --git a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/other/LoadingDialog.kt b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/other/LoadingDialog.kt index 0330f9ce0..537054228 100644 --- a/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/other/LoadingDialog.kt +++ b/core/ui/src/main/kotlin/ru/tech/imageresizershrinker/core/ui/widget/other/LoadingDialog.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -31,6 +32,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.unit.dp @OptIn(ExperimentalMaterial3Api::class) @@ -40,7 +42,13 @@ fun LoadingDialog( canCancel: Boolean = true ) { var showWantDismissDialog by remember(canCancel) { mutableStateOf(false) } - BasicAlertDialog(onDismissRequest = { showWantDismissDialog = canCancel }) { + BasicAlertDialog( + onDismissRequest = { showWantDismissDialog = canCancel } + ) { + val focus = LocalFocusManager.current + LaunchedEffect(focus) { + focus.clearFocus() + } Box( modifier = Modifier .fillMaxSize() @@ -71,7 +79,13 @@ fun LoadingDialog( canCancel: Boolean = true, ) { var showWantDismissDialog by remember(canCancel) { mutableStateOf(false) } - BasicAlertDialog(onDismissRequest = { showWantDismissDialog = canCancel }) { + BasicAlertDialog( + onDismissRequest = { showWantDismissDialog = canCancel } + ) { + val focus = LocalFocusManager.current + LaunchedEffect(focus) { + focus.clearFocus() + } Box( Modifier .fillMaxSize() diff --git a/feature/crop/src/main/java/ru/tech/imageresizershrinker/feature/crop/presentation/CropScreen.kt b/feature/crop/src/main/java/ru/tech/imageresizershrinker/feature/crop/presentation/CropScreen.kt index 0296566eb..d9a47bfda 100644 --- a/feature/crop/src/main/java/ru/tech/imageresizershrinker/feature/crop/presentation/CropScreen.kt +++ b/feature/crop/src/main/java/ru/tech/imageresizershrinker/feature/crop/presentation/CropScreen.kt @@ -23,6 +23,7 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -73,8 +74,10 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -214,6 +217,14 @@ fun CropScreen( ) ) + val focus = LocalFocusManager.current + + LaunchedEffect(scaffoldState.bottomSheetState.currentValue) { + if (scaffoldState.bottomSheetState.currentValue != SheetValue.Expanded) { + focus.clearFocus() + } + } + val controls: @Composable () -> Unit = { Column(Modifier.verticalScroll(rememberScrollState())) { Spacer(modifier = Modifier.height(16.dp)) @@ -394,7 +405,11 @@ fun CropScreen( } Column( - Modifier.weight(0.5f) + Modifier + .weight(0.5f) + .pointerInput(Unit) { + detectTapGestures { focus.clearFocus() } + } ) { controls() } @@ -516,7 +531,12 @@ fun CropScreen( sheetDragHandle = null, sheetShape = RectangleShape, sheetContent = { - Column(Modifier.heightIn(max = screenHeight * 0.7f)) { + Column( + Modifier + .heightIn(max = screenHeight * 0.7f) + .pointerInput(Unit) { + detectTapGestures { focus.clearFocus() } + }) { BottomAppBar( modifier = Modifier.drawHorizontalStroke(true), actions = { diff --git a/feature/single-edit/src/main/java/ru/tech/imageresizershrinker/feature/single_edit/presentation/components/CropEditOption.kt b/feature/single-edit/src/main/java/ru/tech/imageresizershrinker/feature/single_edit/presentation/components/CropEditOption.kt index ff6398c41..3d1a1a308 100644 --- a/feature/single-edit/src/main/java/ru/tech/imageresizershrinker/feature/single_edit/presentation/components/CropEditOption.kt +++ b/feature/single-edit/src/main/java/ru/tech/imageresizershrinker/feature/single_edit/presentation/components/CropEditOption.kt @@ -34,8 +34,10 @@ import androidx.compose.material.icons.rounded.Done import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SheetValue import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -44,6 +46,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.smarttoolfactory.cropper.model.AspectRatio @@ -52,6 +55,7 @@ import com.smarttoolfactory.cropper.settings.CropProperties import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import ru.tech.imageresizershrinker.core.domain.utils.notNullAnd import ru.tech.imageresizershrinker.core.resources.R import ru.tech.imageresizershrinker.core.resources.icons.CropSmall import ru.tech.imageresizershrinker.core.settings.domain.model.DomainAspectRatio @@ -88,7 +92,15 @@ fun CropEditOption( visible = visible, onDismiss = onDismiss, useScaffold = useScaffold, - controls = { + controls = { scaffoldState -> + val focus = LocalFocusManager.current + LaunchedEffect(scaffoldState?.bottomSheetState?.currentValue, focus) { + val current = scaffoldState?.bottomSheetState?.currentValue + if (current.notNullAnd { it != SheetValue.Expanded }) { + focus.clearFocus() + } + } + Spacer(modifier = Modifier.height(16.dp)) AspectRatioSelection( modifier = Modifier