desktop: handling keyboard in auth screen (#2938)

* desktop: handling keyboard in auth screen

* numpad support

* numPadEnter

* padding
This commit is contained in:
Stanislav Dmitrenko 2023-08-16 20:50:27 +03:00 committed by GitHub
parent 34cf672bc6
commit 6cf9f0303b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 89 additions and 43 deletions

View File

@ -39,14 +39,14 @@ private val sharedPreferencesThemes: SharedPreferences by lazy { androidAppConte
actual val settings: Settings by lazy { SharedPreferencesSettings(sharedPreferences) }
actual val settingsThemes: Settings by lazy { SharedPreferencesSettings(sharedPreferencesThemes) }
actual fun screenOrientation(): ScreenOrientation = when (mainActivity.get()?.resources?.configuration?.orientation) {
Configuration.ORIENTATION_PORTRAIT -> ScreenOrientation.PORTRAIT
Configuration.ORIENTATION_LANDSCAPE -> ScreenOrientation.LANDSCAPE
else -> ScreenOrientation.UNDEFINED
actual fun windowOrientation(): WindowOrientation = when (mainActivity.get()?.resources?.configuration?.orientation) {
Configuration.ORIENTATION_PORTRAIT -> WindowOrientation.PORTRAIT
Configuration.ORIENTATION_LANDSCAPE -> WindowOrientation.LANDSCAPE
else -> WindowOrientation.UNDEFINED
}
@Composable
actual fun screenWidth(): Dp = LocalConfiguration.current.screenWidthDp.dp
actual fun windowWidth(): Dp = LocalConfiguration.current.screenWidthDp.dp
actual fun desktopExpandWindowToWidth(width: Dp) {}

View File

@ -311,7 +311,6 @@ fun DesktopScreen(settingsState: SettingsViewState) {
scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() }
}
ModalManager.fullscreen.showInView()
ModalManager.fullscreen.showPasscodeInView()
}
}

View File

@ -19,14 +19,14 @@ expect fun isInNightMode(): Boolean
expect val settings: Settings
expect val settingsThemes: Settings
enum class ScreenOrientation {
enum class WindowOrientation {
UNDEFINED, PORTRAIT, LANDSCAPE
}
expect fun screenOrientation(): ScreenOrientation
expect fun windowOrientation(): WindowOrientation
@Composable
expect fun screenWidth(): Dp
expect fun windowWidth(): Dp
expect fun desktopExpandWindowToWidth(width: Dp)

View File

@ -93,7 +93,7 @@ fun UserPicker(
}
}
val xOffset = with(LocalDensity.current) { 10.dp.roundToPx() }
val maxWidth = with(LocalDensity.current) { screenWidth() * density }
val maxWidth = with(LocalDensity.current) { windowWidth() * density }
Box(Modifier
.fillMaxSize()
.offset { IntOffset(if (newChat.isGone()) -maxWidth.value.roundToInt() else xOffset, 0) }
@ -201,7 +201,7 @@ fun UserProfilePickerItem(u: User, unreadCount: Int = 0, padding: PaddingValues
fun UserProfileRow(u: User) {
Row(
Modifier
.widthIn(max = screenWidth() * 0.7f)
.widthIn(max = windowWidth() * 0.7f)
.padding(vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {

View File

@ -8,6 +8,7 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextAlign
@ -159,13 +160,22 @@ class AlertManager {
title = alertTitle(title),
text = alertText(text),
buttons = {
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
Row(
Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING_HALF),
horizontalArrangement = Arrangement.Center
) {
TextButton(onClick = {
hideAlert()
}) { Text(confirmText, color = Color.Unspecified) }
TextButton(
onClick = {
hideAlert()
},
Modifier.focusRequester(focusRequester)
) {
Text(confirmText, color = Color.Unspecified)
}
}
},
shape = RoundedCornerShape(corner = CornerSize(25.dp))

View File

@ -12,7 +12,7 @@ import dev.icerock.moko.resources.compose.painterResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.*
import chat.simplex.common.platform.screenWidth
import chat.simplex.common.platform.windowWidth
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
@ -238,7 +238,7 @@ fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color
@Composable
fun InfoRowEllipsis(title: String, value: String, onClick: () -> Unit) {
SectionItemViewSpaceBetween(onClick) {
val screenWidthDp = screenWidth()
val screenWidthDp = windowWidth()
Text(title)
Text(
value,

View File

@ -306,10 +306,10 @@ fun IntSize.Companion.Saver(): Saver<IntSize, *> = Saver(
fun DisposableEffectOnGone(always: () -> Unit = {}, whenDispose: () -> Unit = {}, whenGone: () -> Unit) {
DisposableEffect(Unit) {
always()
val orientation = screenOrientation()
val orientation = windowOrientation()
onDispose {
whenDispose()
if (orientation == screenOrientation()) {
if (orientation == windowOrientation()) {
whenGone()
}
}
@ -320,10 +320,10 @@ fun DisposableEffectOnGone(always: () -> Unit = {}, whenDispose: () -> Unit = {}
fun DisposableEffectOnRotate(always: () -> Unit = {}, whenDispose: () -> Unit = {}, whenRotate: () -> Unit) {
DisposableEffect(Unit) {
always()
val orientation = screenOrientation()
val orientation = windowOrientation()
onDispose {
whenDispose()
if (orientation != screenOrientation()) {
if (orientation != windowOrientation()) {
whenRotate()
}
}

View File

@ -6,10 +6,11 @@ import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.*
import androidx.compose.ui.input.key.*
import dev.icerock.moko.resources.compose.painterResource
import androidx.compose.ui.unit.dp
import chat.simplex.common.platform.ScreenOrientation
import chat.simplex.common.platform.screenOrientation
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.DEFAULT_PADDING
import chat.simplex.common.views.helpers.SimpleButton
import chat.simplex.common.views.helpers.*
@ -25,9 +26,43 @@ fun PasscodeView(
submit: () -> Unit,
cancel: () -> Unit,
) {
val focusRequester = remember { FocusRequester() }
@Composable
fun Modifier.handleKeyboard(): Modifier {
val numbers = remember {
arrayOf(
Key.Zero, Key.One, Key.Two, Key.Three, Key.Four, Key.Five, Key.Six, Key.Seven, Key.Eight, Key.Nine,
Key.NumPad0, Key.NumPad1, Key.NumPad2, Key.NumPad3, Key.NumPad4, Key.NumPad5, Key.NumPad6, Key.NumPad7, Key.NumPad8, Key.NumPad9
)
}
return onPreviewKeyEvent {
if (it.key in numbers && it.type == KeyEventType.KeyDown) {
if (passcode.value.length < 16) {
passcode.value += numbers.indexOf(it.key) % 10
}
true
} else if (it.key == Key.Backspace && it.type == KeyEventType.KeyDown && (it.isCtrlPressed || it.isMetaPressed)) {
passcode.value = ""
true
} else if (it.key == Key.Backspace && it.type == KeyEventType.KeyDown) {
passcode.value = passcode.value.dropLast(1)
true
} else if ((it.key == Key.Enter || it.key == Key.NumPadEnter) && it.type == KeyEventType.KeyUp) {
if ((submitEnabled?.invoke(passcode.value) != false && passcode.value.length >= 4)) {
submit()
}
true
} else {
false
}
}
}
@Composable
fun VerticalLayout() {
Column(
Modifier.handleKeyboard().focusRequester(focusRequester),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly
) {
@ -38,7 +73,7 @@ fun PasscodeView(
}
}
PasscodeEntry(passcode, true)
Row {
Row(Modifier.heightIn(min = 70.dp), verticalAlignment = Alignment.CenterVertically) {
SimpleButton(generalGetString(MR.strings.cancel_verb), icon = painterResource(MR.images.ic_close), click = cancel)
Spacer(Modifier.size(20.dp))
SimpleButton(submitLabel, icon = painterResource(MR.images.ic_done_filled), disabled = submitEnabled?.invoke(passcode.value) == false || passcode.value.length < 4, click = submit)
@ -48,9 +83,9 @@ fun PasscodeView(
@Composable
fun HorizontalLayout() {
Row(Modifier.padding(horizontal = DEFAULT_PADDING), horizontalArrangement = Arrangement.Center) {
Row(Modifier.padding(horizontal = DEFAULT_PADDING).handleKeyboard().focusRequester(focusRequester), horizontalArrangement = Arrangement.Center) {
Column(
Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING * 4),
Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
@ -64,7 +99,7 @@ fun PasscodeView(
}
Column(
Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING * 4),
Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
@ -90,9 +125,14 @@ fun PasscodeView(
}
}
if (screenOrientation() == ScreenOrientation.PORTRAIT) {
if (windowOrientation() == WindowOrientation.PORTRAIT || appPlatform.isDesktop) {
VerticalLayout()
} else {
HorizontalLayout()
}
LaunchedEffect(Unit) {
focusRequester.requestFocus()
// Disallow to steal a focus by clicking on buttons or using Tab
focusRequester.captureFocus()
}
}

View File

@ -1,7 +1,6 @@
package chat.simplex.common.views.localauth
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
@ -16,6 +15,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.*
import chat.simplex.common.platform.appPlatform
import chat.simplex.res.MR
@Composable
@ -39,7 +39,7 @@ fun PasscodeEntry(
fun PasscodeView(password: MutableState<String>) {
var showPasscode by rememberSaveable { mutableStateOf(false) }
Text(
if (password.value.isEmpty()) " " else remember(password.value, showPasscode) { splitPassword(showPasscode, password.value) },
if (password.value.isEmpty()) "" else remember(password.value, showPasscode) { splitPassword(showPasscode, password.value) },
Modifier.padding(vertical = 10.dp).clickable { showPasscode = !showPasscode },
style = MaterialTheme.typography.body1
)
@ -47,7 +47,7 @@ fun PasscodeView(password: MutableState<String>) {
@Composable
private fun BoxWithConstraintsScope.VerticalPasswordGrid(password: MutableState<String>) {
val s = minOf(maxWidth, maxHeight) / 4 - 1.dp
val s = if (appPlatform.isAndroid) minOf(maxWidth, maxHeight) / 4 - 1.dp else minOf(minOf(maxWidth, maxHeight) / 4 - 1.dp, 100.dp)
Column(Modifier.width(IntrinsicSize.Min)) {
DigitsRow(s, 1, 2, 3, password)
Divider()

View File

@ -97,7 +97,7 @@ private fun NewChatSheetLayout(
}
}
val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp
val maxWidth = with(LocalDensity.current) { screenWidth() * density }
val maxWidth = with(LocalDensity.current) { windowWidth() * density }
Column(
Modifier
.fillMaxSize()

View File

@ -36,18 +36,15 @@ private val settingsThemesProps =
actual val settings: Settings = PropertiesSettings(settingsProps) { settingsProps.store(settingsFile.writer(), "") }
actual val settingsThemes: Settings = PropertiesSettings(settingsThemesProps) { settingsThemesProps.store(settingsThemesFile.writer(), "") }
actual fun screenOrientation(): ScreenOrientation = ScreenOrientation.UNDEFINED
@Composable // LALAL
actual fun screenWidth(): Dp {
return java.awt.Toolkit.getDefaultToolkit().screenSize.width.dp
/*var width by remember { mutableStateOf(java.awt.Toolkit.getDefaultToolkit().screenSize.width.also { println("LALAL $it") }) }
SideEffect {
if (width != java.awt.Toolkit.getDefaultToolkit().screenSize.width)
width = java.awt.Toolkit.getDefaultToolkit().screenSize.width
actual fun windowOrientation(): WindowOrientation =
if (simplexWindowState.windowState.size.width > simplexWindowState.windowState.size.height) {
WindowOrientation.LANDSCAPE
} else {
WindowOrientation.PORTRAIT
}
return width*/
}// LALAL java.awt.Desktop.getDesktop()
@Composable
actual fun windowWidth(): Dp = simplexWindowState.windowState.size.width
actual fun desktopExpandWindowToWidth(width: Dp) {
if (simplexWindowState.windowState.size.width >= width) return