From 3259c079b4d5d5e770fa8b3cb26e6c2503285129 Mon Sep 17 00:00:00 2001 From: dankito Date: Thu, 26 Sep 2024 05:52:16 +0200 Subject: [PATCH] Fixed dialogs padding --- .../codinux/banking/ui/Platform.android.kt | 4 +++ .../kotlin/net/codinux/banking/ui/Platform.kt | 2 ++ .../codinux/banking/ui/dialogs/BaseDialog.kt | 29 +++++++++++++-- .../ui/extensions/ModifierExtensions.kt | 36 ++++++++++++++++++- .../AccountTransactionDetailsScreen.kt | 2 +- .../ui/screens/BankAccountSettingsScreen.kt | 2 +- .../banking/ui/screens/BankSettingsScreen.kt | 2 +- .../banking/ui/screens/FullscreenViewBase.kt | 19 +++------- .../ui/screens/ProtectAppSettingsDialog.kt | 2 +- .../codinux/banking/ui/Platform.desktop.kt | 4 +++ .../net/codinux/banking/ui/Platform.ios.kt | 20 +++++++++++ .../net/codinux/banking/ui/Platform.js.kt | 4 +++ 12 files changed, 104 insertions(+), 22 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/net/codinux/banking/ui/Platform.android.kt b/composeApp/src/androidMain/kotlin/net/codinux/banking/ui/Platform.android.kt index e8458f3..518f1af 100644 --- a/composeApp/src/androidMain/kotlin/net/codinux/banking/ui/Platform.android.kt +++ b/composeApp/src/androidMain/kotlin/net/codinux/banking/ui/Platform.android.kt @@ -20,6 +20,10 @@ actual fun KeyEvent.isBackButtonPressedEvent(): Boolean = @Composable actual fun systemPaddings(): PaddingValues = PaddingValues(0.dp) +actual fun addKeyboardVisibilityListener(onKeyboardVisibilityChanged: (Boolean) -> Unit) { + // TODO: may implement, but currently only relevant for iOS +} + @Composable actual fun rememberScreenSizeInfo(): ScreenSizeInfo { diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/Platform.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/Platform.kt index 35bbf85..83f76f5 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/Platform.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/Platform.kt @@ -17,6 +17,8 @@ expect fun KeyEvent.isBackButtonPressedEvent(): Boolean @Composable expect fun systemPaddings(): PaddingValues +expect fun addKeyboardVisibilityListener(onKeyboardVisibilityChanged: (Boolean) -> Unit) + @Composable expect fun rememberScreenSizeInfo(): ScreenSizeInfo diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/BaseDialog.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/BaseDialog.kt index 8f23510..af4642f 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/BaseDialog.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/BaseDialog.kt @@ -4,6 +4,11 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material.* 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 +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -11,10 +16,16 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties +import net.codinux.banking.ui.PlatformType +import net.codinux.banking.ui.addKeyboardVisibilityListener +import net.codinux.banking.ui.composables.CloseButton import net.codinux.banking.ui.composables.text.HeaderText import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.DI +import net.codinux.banking.ui.config.Style +import net.codinux.banking.ui.extensions.applyPlatformSpecificPaddingIf import net.codinux.banking.ui.extensions.copy +import net.codinux.banking.ui.extensions.verticalScroll import net.codinux.banking.ui.forms.* @Composable @@ -33,12 +44,15 @@ fun BaseDialog( ) { val overwriteDefaultWidth = useMoreThanPlatformDefaultWidthOnMobile && DI.platform.isMobile + var isKeyboardVisible by remember { mutableStateOf(false) } + + Dialog(onDismissRequest = onDismiss, if (overwriteDefaultWidth) properties.copy(usePlatformDefaultWidth = false) else properties) { RoundedCornersCard(Modifier.let { if (overwriteDefaultWidth) it.fillMaxWidth(0.95f) else it }) { - Column(Modifier.background(Color.White).padding(8.dp)) { + Column(Modifier.applyPlatformSpecificPaddingIf(overwriteDefaultWidth && isKeyboardVisible, 8.dp).background(Color.White).padding(horizontal = 8.dp)) { - Row(Modifier.fillMaxWidth()) { - HeaderText(title, Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 16.dp).weight(1f), textAlign = if (centerTitle) TextAlign.Center else TextAlign.Start) + Row(Modifier.fillMaxWidth().padding(bottom = 8.dp).height(32.dp), verticalAlignment = Alignment.CenterVertically) { + HeaderText(title, Modifier.fillMaxWidth().weight(1f), textColor = Style.ListItemHeaderTextColor, textAlign = if (centerTitle) TextAlign.Center else TextAlign.Start) if (DI.platform.type != PlatformType.Android) { // for iOS it's also relevant due to the missing back gesture / back button CloseButton(onClick = onDismiss) @@ -69,4 +83,13 @@ fun BaseDialog( } } } + + + LaunchedEffect(Unit) { + if (DI.platform.type == PlatformType.iOS) { // on iOS top dialog part gets hidden by top system bar when soft keyboard is visible -> apply system padding then + addKeyboardVisibilityListener { visible -> + isKeyboardVisible = visible + } + } + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/extensions/ModifierExtensions.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/extensions/ModifierExtensions.kt index 214a249..11adf86 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/extensions/ModifierExtensions.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/extensions/ModifierExtensions.kt @@ -2,10 +2,44 @@ package net.codinux.banking.ui.extensions import androidx.compose.foundation.ScrollState import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import net.codinux.banking.ui.systemPaddings +import net.codinux.log.Log fun Modifier.verticalScroll() = this.verticalScroll(ScrollState(0), enabled = true) -fun Modifier.horizontalScroll() = this.horizontalScroll(ScrollState(0), enabled = true) \ No newline at end of file +fun Modifier.horizontalScroll() = this.horizontalScroll(ScrollState(0), enabled = true) + + +@Composable +// we need to support three different cases: +// - normal, non fullscreen dialog, either useMoreThanPlatformDefaultWidthOnMobile is false or soft keyboard is hidden -> apply default vertical padding +// - normal, non fullscreen dialog, useMoreThanPlatformDefaultWidthOnMobile is true and soft keyboard is visible = applyPlatformPadding == true -> on iOS apply platform padding as +// otherwise dialog title gets hidden by upper system bar, on all other platforms default vertical padding +// - fullscreen dialog -> on iOS apply platform padding as otherwise dialog title gets hidden by upper system bar, on all other platforms default vertical padding +fun Modifier.applyPlatformSpecificPaddingIf(applyPlatformPadding: Boolean, minVerticalPadding: Dp = 0.dp): Modifier = + if (applyPlatformPadding) { + this.applyPlatformSpecificPadding(minVerticalPadding) + } else if (minVerticalPadding > 0.dp) { + this.padding(vertical = minVerticalPadding) + } else { + this + } + +@Composable +fun Modifier.applyPlatformSpecificPadding(minVerticalPadding: Dp = 0.dp): Modifier { + val systemPaddings = systemPaddings() + + return this.padding( + top = maxOf(minVerticalPadding, systemPaddings.calculateTopPadding()), + bottom = maxOf(minVerticalPadding, systemPaddings.calculateBottomPadding()) + ).also { + Log.info { "Applied padding: ${systemPaddings.calculateTopPadding()}, ${systemPaddings.calculateBottomPadding()}" } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/AccountTransactionDetailsScreen.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/AccountTransactionDetailsScreen.kt index ac75d03..3427449 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/AccountTransactionDetailsScreen.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/AccountTransactionDetailsScreen.kt @@ -72,7 +72,7 @@ fun AccountTransactionDetailsScreen(transaction: AccountTransactionEntity, onClo onClosed = onClosed ) { SelectionContainer { - Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(8.dp)) { + Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { Column(Modifier.fillMaxWidth()) { SectionHeader(if (isExpense) "Empfänger*in" else "Zahlende*r", false) diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/BankAccountSettingsScreen.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/BankAccountSettingsScreen.kt index 5f5a02d..c9c3815 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/BankAccountSettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/BankAccountSettingsScreen.kt @@ -48,7 +48,7 @@ fun BankAccountSettingsScreen(account: BankAccountEntity, onClosed: () -> Unit) onConfirm = { saveChanges() }, onClosed = onClosed ) { - Column(Modifier.fillMaxSize().verticalScroll().padding(8.dp)) { + Column(Modifier.fillMaxSize().verticalScroll()) { Column { SectionHeader("Einstellungen", false) diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/BankSettingsScreen.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/BankSettingsScreen.kt index cfecdc1..f325909 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/BankSettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/BankSettingsScreen.kt @@ -68,7 +68,7 @@ fun BankSettingsScreen(bank: BankAccessEntity, onClosed: () -> Unit) { onConfirm = { saveChanges() }, onClosed = onClosed ) { - Column(Modifier.fillMaxSize().verticalScroll().padding(8.dp)) { + Column(Modifier.fillMaxSize().verticalScroll()) { Column { OutlinedTextField( label = { Text("Name") }, diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/FullscreenViewBase.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/FullscreenViewBase.kt index 36a11a9..7527bea 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/FullscreenViewBase.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/FullscreenViewBase.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign @@ -17,17 +18,7 @@ import net.codinux.banking.ui.composables.text.HeaderText import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.config.Style -import net.codinux.banking.ui.systemPaddings - -@Composable -private fun Modifier.applyPlatformSpecificPadding(): Modifier { - val systemPaddings = systemPaddings() - - return this.padding( - top = systemPaddings.calculateTopPadding(), - bottom = systemPaddings.calculateBottomPadding() - ) -} +import net.codinux.banking.ui.extensions.applyPlatformSpecificPadding @Composable fun FullscreenViewBase( @@ -45,10 +36,10 @@ fun FullscreenViewBase( onClosed, properties = DialogProperties(usePlatformDefaultWidth = false) ) { - Column(Modifier.fillMaxSize().zIndex(1000f).background(Color.White).applyPlatformSpecificPadding().padding(8.dp)) { + Column(Modifier.fillMaxSize().zIndex(1000f).background(Color.White).applyPlatformSpecificPadding().padding(horizontal = 12.dp)) { - Row(Modifier.fillMaxWidth()) { - HeaderText(title, Modifier.padding(top = 8.dp, bottom = 16.dp).weight(1f), textColor = Style.ListItemHeaderTextColor) + Row(Modifier.fillMaxWidth().padding(top = 12.dp, bottom = 8.dp).height(32.dp), verticalAlignment = Alignment.CenterVertically) { + HeaderText(title, Modifier.weight(1f), textColor = Style.ListItemHeaderTextColor) if (DI.platform.type != PlatformType.Android) { // for iOS it's also relevant due to the missing back gesture / back button CloseButton(onClick = onClosed) diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/ProtectAppSettingsDialog.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/ProtectAppSettingsDialog.kt index cc50c5e..13000f8 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/ProtectAppSettingsDialog.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/ProtectAppSettingsDialog.kt @@ -74,7 +74,7 @@ fun ProtectAppSettingsDialog(appSettings: AppSettings, onClosed: () -> Unit) { FullscreenViewBase("Appzugang schützen", showButtonBar = false, onClosed = onClosed) { - Column(Modifier.fillMaxSize().padding(8.dp)) { + Column(Modifier.fillMaxSize().padding(bottom = 8.dp)) { SegmentedControl(supportedAuthenticationMethods, selectedAuthenticationMethod, Modifier.padding(bottom = 20.dp), getOptionDisplayText = { Internationalization.translate(it) }) { selectedAuthenticationMethod = it diff --git a/composeApp/src/desktopMain/kotlin/net/codinux/banking/ui/Platform.desktop.kt b/composeApp/src/desktopMain/kotlin/net/codinux/banking/ui/Platform.desktop.kt index 1487f34..796b222 100644 --- a/composeApp/src/desktopMain/kotlin/net/codinux/banking/ui/Platform.desktop.kt +++ b/composeApp/src/desktopMain/kotlin/net/codinux/banking/ui/Platform.desktop.kt @@ -20,6 +20,10 @@ actual fun KeyEvent.isBackButtonPressedEvent(): Boolean = false @Composable actual fun systemPaddings(): PaddingValues = PaddingValues(0.dp) +actual fun addKeyboardVisibilityListener(onKeyboardVisibilityChanged: (Boolean) -> Unit) { + // no-op +} + @OptIn(ExperimentalComposeUiApi::class) @Composable diff --git a/composeApp/src/iosMain/kotlin/net/codinux/banking/ui/Platform.ios.kt b/composeApp/src/iosMain/kotlin/net/codinux/banking/ui/Platform.ios.kt index 5f1f872..d2ac533 100644 --- a/composeApp/src/iosMain/kotlin/net/codinux/banking/ui/Platform.ios.kt +++ b/composeApp/src/iosMain/kotlin/net/codinux/banking/ui/Platform.ios.kt @@ -12,8 +12,11 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import platform.CoreGraphics.CGRect +import platform.Foundation.NSNotificationCenter import platform.UIKit.UIApplication import platform.UIKit.UIDevice +import platform.UIKit.UIKeyboardDidShowNotification +import platform.UIKit.UIKeyboardWillHideNotification import platform.UIKit.UIScreen actual val Dispatchers.IOorDefault: CoroutineDispatcher @@ -22,6 +25,23 @@ actual val Dispatchers.IOorDefault: CoroutineDispatcher actual fun KeyEvent.isBackButtonPressedEvent(): Boolean = false // TODO +actual fun addKeyboardVisibilityListener(onKeyboardVisibilityChanged: (Boolean) -> Unit) { + val notificationCenter = NSNotificationCenter.defaultCenter + + notificationCenter.addObserverForName( + name = UIKeyboardDidShowNotification, + `object` = null, + queue = null + ) { _ -> onKeyboardVisibilityChanged(true) } + + notificationCenter.addObserverForName( + name = UIKeyboardWillHideNotification, + `object` = null, + queue = null + ) { _ -> onKeyboardVisibilityChanged(false) } + +} + @Composable @OptIn(ExperimentalForeignApi::class) actual fun systemPaddings(): PaddingValues { diff --git a/composeApp/src/jsMain/kotlin/net/codinux/banking/ui/Platform.js.kt b/composeApp/src/jsMain/kotlin/net/codinux/banking/ui/Platform.js.kt index 1d344c1..9ee5d2a 100644 --- a/composeApp/src/jsMain/kotlin/net/codinux/banking/ui/Platform.js.kt +++ b/composeApp/src/jsMain/kotlin/net/codinux/banking/ui/Platform.js.kt @@ -20,6 +20,10 @@ actual fun KeyEvent.isBackButtonPressedEvent(): Boolean = false @Composable actual fun systemPaddings(): PaddingValues = PaddingValues(0.dp) +actual fun addKeyboardVisibilityListener(onKeyboardVisibilityChanged: (Boolean) -> Unit) { + // no-op +} + @OptIn(ExperimentalComposeUiApi::class) @Composable