Force LTR in some UI, update actions autoclose

This commit is contained in:
Aleksandras Kostarevas 2024-06-15 16:12:26 +03:00
parent b74e977b6a
commit 494e27f482
4 changed files with 185 additions and 128 deletions

View File

@ -61,6 +61,7 @@ import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
@ -403,6 +404,7 @@ fun LazyItemScope.ActionItem(idx: Int, action: Action, onSelect: (Action) -> Uni
context.setSetting( context.setSetting(
ExpandableActionItems, ActionRegistry.moveElement( ExpandableActionItems, ActionRegistry.moveElement(
context.getSetting(ExpandableActionItems, DefaultActionsString), context.getSetting(ExpandableActionItems, DefaultActionsString),
DefaultActions,
action, action,
1 1
) )
@ -414,6 +416,7 @@ fun LazyItemScope.ActionItem(idx: Int, action: Action, onSelect: (Action) -> Uni
context.setSetting( context.setSetting(
ExpandableActionItems, ActionRegistry.moveElement( ExpandableActionItems, ActionRegistry.moveElement(
context.getSetting(ExpandableActionItems, DefaultActionsString), context.getSetting(ExpandableActionItems, DefaultActionsString),
DefaultActions,
action, action,
-1 -1
) )
@ -486,13 +489,11 @@ fun ActionItemSmall(action: Action, onSelect: (Action) -> Unit) {
fun ActionItems(onSelect: (Action) -> Unit) { fun ActionItems(onSelect: (Action) -> Unit) {
val actions = useDataStoreValueBlocking(key = ExpandableActionItems, default = DefaultActionsString) val actions = useDataStoreValueBlocking(key = ExpandableActionItems, default = DefaultActionsString)
if(actions != null) { val actionItems = ActionRegistry.stringToActions(actions, DefaultActions)
val actionItems = ActionRegistry.stringToActions(actions, DefaultActions)
LazyRow { LazyRow {
items(actionItems.size, key = { actionItems[it].name }) { items(actionItems.size, key = { actionItems[it].name }) {
ActionItem(it, actionItems[it], onSelect) ActionItem(it, actionItems[it], onSelect)
}
} }
} }
} }
@ -588,7 +589,8 @@ fun ActionBar(
inlineSuggestions: List<MutableState<View?>>, inlineSuggestions: List<MutableState<View?>>,
forceOpenActionsInitially: Boolean = false, forceOpenActionsInitially: Boolean = false,
importantNotice: ImportantNotice? = null, importantNotice: ImportantNotice? = null,
keyboardManagerForAction: KeyboardManagerForAction? = null keyboardManagerForAction: KeyboardManagerForAction? = null,
actionsForcedOpenByUser: MutableState<Boolean> = mutableStateOf(false)
) { ) {
val view = LocalView.current val view = LocalView.current
val context = LocalContext.current val context = LocalContext.current
@ -600,14 +602,16 @@ fun ActionBar(
} }
LaunchedEffect(words) { LaunchedEffect(words) {
if(words != null && !words.isEmpty) { if(words != null && !words.isEmpty && !actionsForcedOpenByUser.value) {
isActionsOpen.value = false isActionsOpen.value = false
actionsForcedOpenByUser.value = false
} }
} }
LaunchedEffect(inlineSuggestions) { LaunchedEffect(inlineSuggestions) {
if(inlineSuggestions.isNotEmpty()) { if(inlineSuggestions.isNotEmpty()) {
isActionsOpen.value = false isActionsOpen.value = false
actionsForcedOpenByUser.value = false
} }
} }
@ -618,6 +622,8 @@ fun ActionBar(
Row { Row {
ExpandActionsButton(isActionsOpen.value) { ExpandActionsButton(isActionsOpen.value) {
isActionsOpen.value = !isActionsOpen.value isActionsOpen.value = !isActionsOpen.value
actionsForcedOpenByUser.value = isActionsOpen.value
if(isActionsOpen.value && importantNotice != null) { if(isActionsOpen.value && importantNotice != null) {
importantNotice.onDismiss(context) importantNotice.onDismiss(context)
} }

View File

@ -44,8 +44,10 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -259,6 +261,8 @@ class UixManager(private val latinIME: LatinIME) {
private var numSuggestionsSinceNotice = 0 private var numSuggestionsSinceNotice = 0
private var currentNotice: MutableState<ImportantNotice?> = mutableStateOf(null) private var currentNotice: MutableState<ImportantNotice?> = mutableStateOf(null)
private val actionsForcedOpenByUser = mutableStateOf(false)
var currWindowActionWindow: ActionWindow? = null var currWindowActionWindow: ActionWindow? = null
val isMainKeyboardHidden get() = mainKeyboardHidden val isMainKeyboardHidden get() = mainKeyboardHidden
@ -295,7 +299,8 @@ class UixManager(private val latinIME: LatinIME) {
inlineSuggestions = inlineSuggestions, inlineSuggestions = inlineSuggestions,
onActionActivated = { onActionActivated(it) }, onActionActivated = { onActionActivated(it) },
importantNotice = currentNotice.value, importantNotice = currentNotice.value,
keyboardManagerForAction = keyboardManagerForAction keyboardManagerForAction = keyboardManagerForAction,
actionsForcedOpenByUser = actionsForcedOpenByUser
) )
} }
} }
@ -319,6 +324,7 @@ class UixManager(private val latinIME: LatinIME) {
setContent() setContent()
actionsForcedOpenByUser.value = false
keyboardManagerForAction.announce("${latinIME.resources.getString(action.name)} mode") keyboardManagerForAction.announce("${latinIME.resources.getString(action.name)} mode")
} }
@ -340,6 +346,7 @@ class UixManager(private val latinIME: LatinIME) {
setContent() setContent()
actionsForcedOpenByUser.value = false
keyboardManagerForAction.announce("$name closed") keyboardManagerForAction.announce("$name closed")
} }
@ -491,25 +498,27 @@ class UixManager(private val latinIME: LatinIME) {
composeView?.setContent { composeView?.setContent {
UixThemeWrapper(latinIME.colorScheme) { UixThemeWrapper(latinIME.colorScheme) {
CompositionLocalProvider(LocalManager provides keyboardManagerForAction) { CompositionLocalProvider(LocalManager provides keyboardManagerForAction) {
Column { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr ) {
Spacer(modifier = Modifier.weight(1.0f)) Column {
Surface(modifier = Modifier.onSizeChanged { Spacer(modifier = Modifier.weight(1.0f))
latinIME.updateTouchableHeight(it.height) Surface(modifier = Modifier.onSizeChanged {
}, color = latinIME.keyboardColor) { latinIME.updateTouchableHeight(it.height)
Box { }, color = latinIME.keyboardColor) {
Column { Box {
when { Column {
currWindowActionWindow != null -> ActionViewWithHeader( when {
currWindowActionWindow!! currWindowActionWindow != null -> ActionViewWithHeader(
) currWindowActionWindow!!
)
else -> MainKeyboardViewWithActionBar() else -> MainKeyboardViewWithActionBar()
}
latinIME.LegacyKeyboardView(hidden = isMainKeyboardHidden)
} }
latinIME.LegacyKeyboardView(hidden = isMainKeyboardHidden) ForgetWordDialog()
} }
ForgetWordDialog()
} }
} }
} }
@ -606,6 +615,7 @@ class UixManager(private val latinIME: LatinIME) {
fun onInputFinishing() { fun onInputFinishing() {
closeActionWindow() closeActionWindow()
actionsForcedOpenByUser.value = false
languageSwitcherDialog?.dismiss() languageSwitcherDialog?.dismiss()
} }

View File

@ -45,11 +45,13 @@ import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.longPreferencesKey
@ -75,6 +77,8 @@ import org.futo.inputmethod.latin.uix.settings.ScrollableList
import org.futo.inputmethod.latin.uix.settings.useDataStore import org.futo.inputmethod.latin.uix.settings.useDataStore
import org.futo.inputmethod.latin.uix.settings.useDataStoreValueBlocking import org.futo.inputmethod.latin.uix.settings.useDataStoreValueBlocking
import org.futo.inputmethod.latin.uix.theme.Typography import org.futo.inputmethod.latin.uix.theme.Typography
import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
import org.futo.inputmethod.latin.uix.theme.presets.DynamicDarkTheme
import org.futo.inputmethod.updates.dismissedMigrateUpdateNotice import org.futo.inputmethod.updates.dismissedMigrateUpdateNotice
import org.futo.inputmethod.updates.openURI import org.futo.inputmethod.updates.openURI
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -151,35 +155,37 @@ fun IconText(icon: Painter, title: String, body: String) {
fun PaymentText(verbose: Boolean) { fun PaymentText(verbose: Boolean) {
val numDaysInstalled = useNumberOfDaysInstalled() val numDaysInstalled = useNumberOfDaysInstalled()
// Doesn't make sense to say "You've been using for ... days" if it's less than seven days CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
if(numDaysInstalled.intValue >= 7) { // Doesn't make sense to say "You've been using for ... days" if it's less than seven days
ParagraphText(stringResource(R.string.payment_text_1, numDaysInstalled.value)) if (numDaysInstalled.intValue >= 7) {
} else { ParagraphText(stringResource(R.string.payment_text_1, numDaysInstalled.value))
ParagraphText(stringResource(R.string.payment_text_1_alt)) } else {
} ParagraphText(stringResource(R.string.payment_text_1_alt))
}
if(verbose) { if (verbose) {
IconText( IconText(
icon = painterResource(id = R.drawable.activity), icon = painterResource(id = R.drawable.activity),
title = "Sustainable Development", title = "Sustainable Development",
body = "FUTO's mission is for open-source software and non-malicious software business practices to become a sustainable income source for projects and their developers. For this reason, we are in favor of users actually paying for software." body = "FUTO's mission is for open-source software and non-malicious software business practices to become a sustainable income source for projects and their developers. For this reason, we are in favor of users actually paying for software."
) )
IconText( IconText(
icon = painterResource(id = R.drawable.unlock), icon = painterResource(id = R.drawable.unlock),
title = "Commitment to Privacy", title = "Commitment to Privacy",
body = "This app will never serve you ads or sell your data. We are not in the business of doing that." body = "This app will never serve you ads or sell your data. We are not in the business of doing that."
) )
/* /*
IconText( IconText(
icon = painterResource(id = R.drawable.code), icon = painterResource(id = R.drawable.code),
title = "Ongoing Work", title = "Ongoing Work",
body = "Creating and maintaining great software requires significant resources. Your support will help us keep development going." body = "Creating and maintaining great software requires significant resources. Your support will help us keep development going."
) )
*/ */
} else { } else {
ParagraphText(stringResource(R.string.payment_text_2)) ParagraphText(stringResource(R.string.payment_text_2))
}
} }
} }
@ -427,7 +433,7 @@ fun PaymentSurface(isPrimary: Boolean, title: String, onClick: (() -> Unit)? = n
} }
@Composable @Composable
@Preview(showBackground = true, heightDp = 10000) @Preview(showBackground = true, heightDp = 2048)
fun PaymentScreen( fun PaymentScreen(
navController: NavHostController = rememberNavController(), navController: NavHostController = rememberNavController(),
onExit: () -> Unit = { } onExit: () -> Unit = { }
@ -515,39 +521,41 @@ fun PaymentScreen(
val lastValidRemindValue = remember { mutableFloatStateOf(5.0f) } val lastValidRemindValue = remember { mutableFloatStateOf(5.0f) }
val remindDays = remember { mutableStateOf("5") } val remindDays = remember { mutableStateOf("5") }
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
Button( CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
onClick = { Button(
coroutineScope.launch { onClick = {
pushNoticeReminderTime(context, lastValidRemindValue.floatValue) coroutineScope.launch {
} pushNoticeReminderTime(context, lastValidRemindValue.floatValue)
onExit() }
}, onExit()
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
),
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
Text(stringResource(R.string.remind_me_in_x))
BasicTextField(
value = remindDays.value,
onValueChange = {
remindDays.value = it
it.toFloatOrNull()
?.let { lastValidRemindValue.floatValue = it }
}, },
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
),
modifier = Modifier modifier = Modifier
.width(32.dp) .fillMaxWidth()
.border(Dp.Hairline, LocalContentColor.current) .padding(8.dp)
.padding(4.dp), ) {
textStyle = Typography.bodyMedium.copy(color = LocalContentColor.current), Text(stringResource(R.string.remind_me_in_x))
cursorBrush = SolidColor(LocalContentColor.current), BasicTextField(
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number) value = remindDays.value,
) onValueChange = {
Text(stringResource(R.string.in_x_days)) remindDays.value = it
it.toFloatOrNull()
?.let { lastValidRemindValue.floatValue = it }
},
modifier = Modifier
.width(32.dp)
.border(Dp.Hairline, LocalContentColor.current)
.padding(4.dp),
textStyle = Typography.bodyMedium.copy(color = LocalContentColor.current),
cursorBrush = SolidColor(LocalContentColor.current),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number)
)
Text(stringResource(R.string.in_x_days))
}
} }
} }
} }
@ -558,6 +566,17 @@ fun PaymentScreen(
} }
} }
@Composable
@Preview(heightDp = 2048)
private fun PaymentScreenDark() {
val context = LocalContext.current
UixThemeWrapper(colorScheme = DynamicDarkTheme.obtainColors(context)) {
Surface(color = MaterialTheme.colorScheme.background) {
PaymentScreen()
}
}
}
@Composable @Composable
fun PaymentScreenSwitch( fun PaymentScreenSwitch(

View File

@ -4,6 +4,7 @@ import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -24,6 +25,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -31,10 +33,12 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.Path
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.futo.inputmethod.latin.uix.KeyBordersSetting import org.futo.inputmethod.latin.uix.KeyBordersSetting
import org.futo.inputmethod.latin.uix.KeyHintsSetting import org.futo.inputmethod.latin.uix.KeyHintsSetting
@ -234,65 +238,83 @@ fun ThemePicker(onSelected: (ThemeOption) -> Unit) {
} }
} }
val originalDirection = LocalLayoutDirection.current
Column { Column {
LazyVerticalGrid( CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
modifier = Modifier.fillMaxWidth(), LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 172.dp) modifier = Modifier.fillMaxWidth(),
) { columns = GridCells.Adaptive(minSize = 172.dp),
items(availableThemeOptions.count()) { horizontalArrangement = if (LocalLayoutDirection.current == LayoutDirection.Rtl) {
val themeOption = availableThemeOptions[it].second Arrangement.End
} else {
ThemePreview(themeOption, isSelected = themeOption.key == currentTheme) { Arrangement.Start
onSelected(themeOption)
} }
} ) {
items(availableThemeOptions.count()) {
val themeOption = availableThemeOptions[it].second
item { ThemePreview(themeOption, isSelected = themeOption.key == currentTheme) {
AddCustomThemeButton { onSelected(themeOption)
// TODO: Custom themes }
val toast = Toast.makeText(
context,
"Custom themes coming eventually",
Toast.LENGTH_SHORT
)
toast.show()
} }
}
item(span = { GridItemSpan(maxCurrentLineSpan) }) { } item {
AddCustomThemeButton {
// TODO: Custom themes
val toast = Toast.makeText(
context,
"Custom themes coming eventually",
Toast.LENGTH_SHORT
)
toast.show()
}
}
item(span = { GridItemSpan(maxCurrentLineSpan) }) { item(span = { GridItemSpan(maxCurrentLineSpan) }) { }
SettingToggleDataStore(
title = "Key borders",
setting = KeyBordersSetting
)
}
item(span = { GridItemSpan(maxCurrentLineSpan) }) {
SettingToggleDataStore(
title = "Show symbol hints",
setting = KeyHintsSetting
)
}
item(span = { GridItemSpan(maxCurrentLineSpan) }) { item(span = { GridItemSpan(maxCurrentLineSpan) }) {
SettingSlider( CompositionLocalProvider(LocalLayoutDirection provides originalDirection) {
title = "Keyboard Height", SettingToggleDataStore(
setting = KeyboardHeightMultiplierSetting, title = "Key borders",
range = 0.33f .. 1.75f, transform = { it }, setting = KeyBordersSetting
indicator = { "${(it * 100.0f).roundToInt()}%" }, )
steps = 16 }
) }
} item(span = { GridItemSpan(maxCurrentLineSpan) }) {
item(span = { GridItemSpan(maxCurrentLineSpan) }) { CompositionLocalProvider(LocalLayoutDirection provides originalDirection) {
SettingSlider( SettingToggleDataStore(
title = "Keyboard Offset", title = "Show symbol hints",
setting = KeyboardBottomOffsetSetting, setting = KeyHintsSetting
range = 0.0f .. 50.0f, )
hardRange = 0.0f .. 250.0f, }
transform = { it }, }
indicator = { "${String.format("%.1f", it)} dp" },
steps = 9 item(span = { GridItemSpan(maxCurrentLineSpan) }) {
) CompositionLocalProvider(LocalLayoutDirection provides originalDirection) {
SettingSlider(
title = "Keyboard Height",
setting = KeyboardHeightMultiplierSetting,
range = 0.33f..1.75f, transform = { it },
indicator = { "${(it * 100.0f).roundToInt()}%" },
steps = 16
)
}
}
item(span = { GridItemSpan(maxCurrentLineSpan) }) {
CompositionLocalProvider(LocalLayoutDirection provides originalDirection) {
SettingSlider(
title = "Keyboard Offset",
setting = KeyboardBottomOffsetSetting,
range = 0.0f..50.0f,
hardRange = 0.0f..250.0f,
transform = { it },
indicator = { "${String.format("%.1f", it)} dp" },
steps = 9
)
}
}
} }
} }
} }