Add MultiSliderDialog (#1580)

This commit is contained in:
Helium314 2025-06-02 20:33:10 +02:00 committed by GitHub
parent d951a1dbdd
commit 7dbf5ea86d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 367 additions and 109 deletions

View file

@ -482,7 +482,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setOneHandedModeEnabled(boolean enabled) {
if (mKeyboardViewWrapper.getOneHandedModeEnabled() == enabled) {
setOneHandedModeEnabled(enabled, false);
}
public void setOneHandedModeEnabled(boolean enabled, boolean force) {
if (!force && mKeyboardViewWrapper.getOneHandedModeEnabled() == enabled) {
return;
}
final Settings settings = Settings.getInstance();
@ -515,6 +519,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
!settings.getCurrent().mIsSplitKeyboardEnabled,
mCurrentOrientation == Configuration.ORIENTATION_LANDSCAPE
);
setOneHandedModeEnabled(settings.getCurrent().mOneHandedModeEnabled, true);
reloadKeyboard();
}

View file

@ -18,6 +18,7 @@ import helium314.keyboard.latin.settings.Defaults
import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.settings.SettingsSubtype
import helium314.keyboard.latin.settings.SettingsSubtype.Companion.toSettingsSubtype
import helium314.keyboard.latin.settings.createPrefKeyForBooleanSettings
import helium314.keyboard.latin.utils.DeviceProtectedUtils
import helium314.keyboard.latin.utils.DictionaryInfoUtils
import helium314.keyboard.latin.utils.DictionaryInfoUtils.USER_DICTIONARY_SUFFIX
@ -562,6 +563,45 @@ fun checkVersionUpgrade(context: Context) {
prefs.edit().putString(it.key, newValue).apply()
}
}
if (oldVersion <= 3101) {
val e = prefs.edit()
prefs.all.toMap().forEach { (key, value) ->
if (key == "side_padding_scale") {
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SIDE_PADDING_SCALE_PREFIX, 0, 2), value as Float)
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SIDE_PADDING_SCALE_PREFIX, 2, 2), value)
} else if (key == "side_padding_scale_landscape") {
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SIDE_PADDING_SCALE_PREFIX, 1, 2), value as Float)
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SIDE_PADDING_SCALE_PREFIX, 3, 2), value)
} else if (key == "bottom_padding_scale") {
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_BOTTOM_PADDING_SCALE_PREFIX, 0, 1), value as Float)
} else if (key == "bottom_padding_scale_landscape") {
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_BOTTOM_PADDING_SCALE_PREFIX, 1, 1), value as Float)
} else if (key == "split_spacer_scale") {
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SPLIT_SPACER_SCALE_PREFIX, 0, 1), value as Float)
} else if (key == "split_spacer_scale_landscape") {
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_SPLIT_SPACER_SCALE_PREFIX, 1, 1), value as Float)
} else if (key == "one_handed_mode_enabled_p_true") {
e.putBoolean(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_MODE_PREFIX, 0, 2), value as Boolean)
} else if (key == "one_handed_mode_enabled_p_false") {
e.putBoolean(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_MODE_PREFIX, 1, 2), value as Boolean)
} else if (key == "one_handed_mode_scale_p_true") {
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_SCALE_PREFIX, 0, 2), value as Float)
} else if (key == "one_handed_mode_scale_p_false") {
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_SCALE_PREFIX, 1, 2), value as Float)
} else if (key == "one_handed_mode_gravity_p_true") {
e.putInt(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_GRAVITY_PREFIX, 0, 2), value as Int)
} else if (key == "one_handed_mode_gravity_p_false") {
e.putInt(createPrefKeyForBooleanSettings(Settings.PREF_ONE_HANDED_GRAVITY_PREFIX, 1, 2), value as Int)
} else if (key == "keyboard_height_scale") {
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_KEYBOARD_HEIGHT_SCALE_PREFIX, 1, 1), value as Float)
e.putFloat(createPrefKeyForBooleanSettings(Settings.PREF_KEYBOARD_HEIGHT_SCALE_PREFIX, 1, 1), value)
} else {
return@forEach
}
e.remove(key)
}
e.apply()
}
upgradeToolbarPrefs(prefs)
LayoutUtilsCustom.onLayoutFileChanged() // just to be sure
prefs.edit { putInt(Settings.PREF_VERSION_CODE, BuildConfig.VERSION_CODE) }

View file

@ -78,12 +78,13 @@ class KeyboardWrapperView @JvmOverloads constructor(
val changePercent = 2 * sign * (x - motionEvent.rawX) / context.resources.displayMetrics.density
if (abs(changePercent) < 1) return@setOnTouchListener true
x = motionEvent.rawX
val oldScale = Settings.readOneHandedModeScale(context.prefs(), Settings.getValues().mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE)
val landscape = Settings.getValues().mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE
val split = Settings.getValues().mIsSplitKeyboardEnabled
val oldScale = Settings.readOneHandedModeScale(context.prefs(), landscape, split)
val newScale = (oldScale + changePercent / 100f).coerceAtMost(2.5f).coerceAtLeast(0.5f)
if (newScale == oldScale) return@setOnTouchListener true
Settings.getInstance().writeOneHandedModeScale(newScale)
oneHandedModeEnabled = false // intentionally putting wrong value, so KeyboardSwitcher.setOneHandedModeEnabled does actually reload
KeyboardSwitcher.getInstance().setOneHandedModeEnabled(true)
KeyboardSwitcher.getInstance().setOneHandedModeEnabled(true, true)
}
else -> x = 0f
}

View file

@ -44,6 +44,7 @@ object Defaults {
LayoutType.CLIPBOARD_BOTTOM -> "clip_bottom_row"
}
private const val DEFAULT_SIZE_SCALE = 1.0f // 100%
const val PREF_THEME_STYLE = KeyboardTheme.STYLE_MATERIAL
const val PREF_ICON_STYLE = KeyboardTheme.STYLE_MATERIAL
const val PREF_THEME_COLORS = KeyboardTheme.THEME_LIGHT
@ -80,15 +81,16 @@ object Defaults {
"hu${Separators.SET}${ExtraValue.KEYBOARD_LAYOUT_SET}=MAIN:qwerty"
const val PREF_ENABLE_SPLIT_KEYBOARD = false
const val PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE = false
const val PREF_SPLIT_SPACER_SCALE = SettingsValues.DEFAULT_SIZE_SCALE
const val PREF_SPLIT_SPACER_SCALE_LANDSCAPE = SettingsValues.DEFAULT_SIZE_SCALE
const val PREF_KEYBOARD_HEIGHT_SCALE = SettingsValues.DEFAULT_SIZE_SCALE
const val PREF_BOTTOM_PADDING_SCALE = SettingsValues.DEFAULT_SIZE_SCALE
const val PREF_BOTTOM_PADDING_SCALE_LANDSCAPE = 0f
const val PREF_SIDE_PADDING_SCALE = 0f
const val PREF_SIDE_PADDING_SCALE_LANDSCAPE = 0f
const val PREF_FONT_SCALE = SettingsValues.DEFAULT_SIZE_SCALE
const val PREF_EMOJI_FONT_SCALE = SettingsValues.DEFAULT_SIZE_SCALE
@JvmField
val PREF_SPLIT_SPACER_SCALE = Array(2) { DEFAULT_SIZE_SCALE }
@JvmField
val PREF_KEYBOARD_HEIGHT_SCALE = Array(2) { DEFAULT_SIZE_SCALE }
@JvmField
val PREF_BOTTOM_PADDING_SCALE = arrayOf(DEFAULT_SIZE_SCALE, 0f)
@JvmField
val PREF_SIDE_PADDING_SCALE = Array(4) { 0f }
const val PREF_FONT_SCALE = DEFAULT_SIZE_SCALE
const val PREF_EMOJI_FONT_SCALE = DEFAULT_SIZE_SCALE
const val PREF_EMOJI_KEY_FIT = true
const val PREF_EMOJI_SKIN_TONE = ""
const val PREF_SPACE_HORIZONTAL_SWIPE = "move_cursor"

View file

@ -87,13 +87,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_ADDITIONAL_SUBTYPES = "additional_subtypes";
public static final String PREF_ENABLE_SPLIT_KEYBOARD = "split_keyboard";
public static final String PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE = "split_keyboard_landscape";
public static final String PREF_SPLIT_SPACER_SCALE = "split_spacer_scale";
public static final String PREF_SPLIT_SPACER_SCALE_LANDSCAPE = "split_spacer_scale_landscape";
public static final String PREF_KEYBOARD_HEIGHT_SCALE = "keyboard_height_scale";
public static final String PREF_BOTTOM_PADDING_SCALE = "bottom_padding_scale";
public static final String PREF_BOTTOM_PADDING_SCALE_LANDSCAPE = "bottom_padding_scale_landscape";
public static final String PREF_SIDE_PADDING_SCALE = "side_padding_scale";
public static final String PREF_SIDE_PADDING_SCALE_LANDSCAPE = "side_padding_scale_landscape";
public static final String PREF_SPLIT_SPACER_SCALE_PREFIX = "split_spacer_scale";
public static final String PREF_KEYBOARD_HEIGHT_SCALE_PREFIX = "keyboard_height_scale";
public static final String PREF_BOTTOM_PADDING_SCALE_PREFIX = "bottom_padding_scale";
public static final String PREF_SIDE_PADDING_SCALE_PREFIX = "side_padding_scale";
public static final String PREF_FONT_SCALE = "font_scale";
public static final String PREF_EMOJI_FONT_SCALE = "emoji_font_scale";
public static final String PREF_EMOJI_KEY_FIT = "emoji_key_fit";
@ -126,10 +123,9 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_USE_APPS = "use_apps";
public static final String PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD = "long_press_symbols_for_numpad";
// one-handed mode gravity, enablement and scale, stored separately per orientation
public static final String PREF_ONE_HANDED_MODE_PREFIX = "one_handed_mode_enabled_p_";
public static final String PREF_ONE_HANDED_GRAVITY_PREFIX = "one_handed_mode_gravity_p_";
public static final String PREF_ONE_HANDED_SCALE_PREFIX = "one_handed_mode_scale_p_";
public static final String PREF_ONE_HANDED_MODE_PREFIX = "one_handed_mode_enabled";
public static final String PREF_ONE_HANDED_GRAVITY_PREFIX = "one_handed_mode_gravity";
public static final String PREF_ONE_HANDED_SCALE_PREFIX = "one_handed_mode_scale";
public static final String PREF_SHOW_NUMBER_ROW = "show_number_row";
public static final String PREF_LOCALIZED_NUMBER_ROW = "localized_number_row";
@ -365,31 +361,43 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return prefs.getBoolean(PREF_SHOW_SETUP_WIZARD_ICON, Defaults.PREF_SHOW_SETUP_WIZARD_ICON);
}
public static boolean readOneHandedModeEnabled(final SharedPreferences prefs, final boolean isLandscape) {
return prefs.getBoolean(PREF_ONE_HANDED_MODE_PREFIX + !isLandscape, Defaults.PREF_ONE_HANDED_MODE);
public static boolean readOneHandedModeEnabled(final SharedPreferences prefs, final boolean landscape, final boolean split) {
final int index = SettingsKt.findIndexOfDefaultSetting(landscape, split);
final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_MODE_PREFIX, index, 2);
return prefs.getBoolean(key, Defaults.PREF_ONE_HANDED_MODE);
}
public void writeOneHandedModeEnabled(final boolean enabled) {
mPrefs.edit().putBoolean(PREF_ONE_HANDED_MODE_PREFIX +
(mSettingsValues.mDisplayOrientation != Configuration.ORIENTATION_LANDSCAPE), enabled).apply();
final boolean landscape = mSettingsValues.mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE;
final int index = SettingsKt.findIndexOfDefaultSetting(landscape, mSettingsValues.mIsSplitKeyboardEnabled);
final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_MODE_PREFIX, index, 2);
mPrefs.edit().putBoolean(key, enabled).apply();
}
public static float readOneHandedModeScale(final SharedPreferences prefs, final boolean isLandscape) {
return prefs.getFloat(PREF_ONE_HANDED_SCALE_PREFIX + !isLandscape, Defaults.PREF_ONE_HANDED_SCALE);
public static float readOneHandedModeScale(final SharedPreferences prefs, final boolean landscape, final boolean split) {
final int index = SettingsKt.findIndexOfDefaultSetting(landscape, split);
final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_SCALE_PREFIX, index, 2);
return prefs.getFloat(key, Defaults.PREF_ONE_HANDED_SCALE);
}
public void writeOneHandedModeScale(final Float scale) {
mPrefs.edit().putFloat(PREF_ONE_HANDED_SCALE_PREFIX +
(mSettingsValues.mDisplayOrientation != Configuration.ORIENTATION_LANDSCAPE), scale).apply();
final boolean landscape = mSettingsValues.mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE;
final int index = SettingsKt.findIndexOfDefaultSetting(landscape, mSettingsValues.mIsSplitKeyboardEnabled);
final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_SCALE_PREFIX, index, 2);
mPrefs.edit().putFloat(key, scale).apply();
}
public static int readOneHandedModeGravity(final SharedPreferences prefs, final boolean isLandscape) {
return prefs.getInt(PREF_ONE_HANDED_GRAVITY_PREFIX + !isLandscape, Defaults.PREF_ONE_HANDED_GRAVITY);
public static int readOneHandedModeGravity(final SharedPreferences prefs, final boolean landscape, final boolean split) {
final int index = SettingsKt.findIndexOfDefaultSetting(landscape, split);
final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_GRAVITY_PREFIX, index, 2);
return prefs.getInt(key, Defaults.PREF_ONE_HANDED_GRAVITY);
}
public void writeOneHandedModeGravity(final int gravity) {
mPrefs.edit().putInt(PREF_ONE_HANDED_GRAVITY_PREFIX +
(mSettingsValues.mDisplayOrientation != Configuration.ORIENTATION_LANDSCAPE), gravity).apply();
final boolean landscape = mSettingsValues.mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE;
final int index = SettingsKt.findIndexOfDefaultSetting(landscape, mSettingsValues.mIsSplitKeyboardEnabled);
final String key = SettingsKt.createPrefKeyForBooleanSettings(PREF_ONE_HANDED_GRAVITY_PREFIX, index, 2);
mPrefs.edit().putInt(key, gravity).apply();
}
public void writeSplitKeyboardEnabled(final boolean enabled, final boolean isLandscape) {
@ -402,21 +410,32 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return prefs.getBoolean(pref, isLandscape ? Defaults.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE : Defaults.PREF_ENABLE_SPLIT_KEYBOARD);
}
public static float readSplitSpacerScale(final SharedPreferences prefs, final boolean isLandscape) {
final String pref = isLandscape ? PREF_SPLIT_SPACER_SCALE_LANDSCAPE : PREF_SPLIT_SPACER_SCALE;
return prefs.getFloat(pref, isLandscape ? Defaults.PREF_SPLIT_SPACER_SCALE_LANDSCAPE : Defaults.PREF_SPLIT_SPACER_SCALE);
public static float readSplitSpacerScale(final SharedPreferences prefs, final boolean landscape) {
final int index = SettingsKt.findIndexOfDefaultSetting(landscape);
final Float[] defaults = Defaults.PREF_SPLIT_SPACER_SCALE;
final float defaultValue = defaults[index];
return prefs.getFloat(SettingsKt.createPrefKeyForBooleanSettings(PREF_SPLIT_SPACER_SCALE_PREFIX, index, 1), defaultValue);
}
public static float readBottomPaddingScale(final SharedPreferences prefs, final boolean landscape) {
if (landscape)
return prefs.getFloat(PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, Defaults.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE);
return prefs.getFloat(PREF_BOTTOM_PADDING_SCALE, Defaults.PREF_BOTTOM_PADDING_SCALE);
final int index = SettingsKt.findIndexOfDefaultSetting(landscape);
final Float[] defaults = Defaults.PREF_BOTTOM_PADDING_SCALE;
final float defaultValue = defaults[index];
return prefs.getFloat(SettingsKt.createPrefKeyForBooleanSettings(PREF_BOTTOM_PADDING_SCALE_PREFIX, index, 1), defaultValue);
}
public static float readSidePaddingScale(final SharedPreferences prefs, final boolean landscape) {
if (landscape)
return prefs.getFloat(PREF_SIDE_PADDING_SCALE_LANDSCAPE, Defaults.PREF_SIDE_PADDING_SCALE_LANDSCAPE);
return prefs.getFloat(PREF_SIDE_PADDING_SCALE, Defaults.PREF_SIDE_PADDING_SCALE);
public static float readSidePaddingScale(final SharedPreferences prefs, final boolean landscape, final boolean split) {
final int index = SettingsKt.findIndexOfDefaultSetting(landscape, split);
final Float[] defaults = Defaults.PREF_SIDE_PADDING_SCALE;
final float defaultValue = defaults[index];
return prefs.getFloat(SettingsKt.createPrefKeyForBooleanSettings(PREF_SIDE_PADDING_SCALE_PREFIX, index, 2), defaultValue);
}
public static float readHeightScale(final SharedPreferences prefs, final boolean landscape) {
final int index = SettingsKt.findIndexOfDefaultSetting(landscape);
final Float[] defaults = Defaults.PREF_KEYBOARD_HEIGHT_SCALE;
final float defaultValue = defaults[index];
return prefs.getFloat(SettingsKt.createPrefKeyForBooleanSettings(PREF_KEYBOARD_HEIGHT_SCALE_PREFIX, index, 1), defaultValue);
}
public static boolean readHasHardwareKeyboard(final Configuration conf) {

View file

@ -14,3 +14,13 @@ fun customIconIds(context: Context, prefs: SharedPreferences) = customIconNames(
val id = runCatching { context.resources.getIdentifier(entry.value, "drawable", context.packageName) }.getOrNull()
id?.let { entry.key to it }
}
/** Derive an index from a number of boolean [settingValues], used to access the matching default value in a defaults arraY */
fun findIndexOfDefaultSetting(vararg settingValues: Boolean): Int {
var i = -1
return settingValues.sumOf { i++; if (it) 1.shl(i) else 0 }
}
/** Create pref key that is derived from a [number] of boolean conditions. The [index] is as created by [findIndexOfDefaultSetting]. */
fun createPrefKeyForBooleanSettings(prefix: String, index: Int, number: Int): String =
"${prefix}_${Array(number) { index.shr(it) % 2 == 1 }.joinToString("_")}"

View file

@ -42,8 +42,6 @@ import java.util.Locale;
*/
// Non-final for testing via mock library.
public class SettingsValues {
public static final float DEFAULT_SIZE_SCALE = 1.0f; // 100%
// From resources:
public final SpacingAndPunctuations mSpacingAndPunctuations;
public final long mDoubleSpacePeriodTimeout;
@ -242,7 +240,7 @@ public class SettingsValues {
mSecondaryStripVisible = mToolbarMode != ToolbarMode.HIDDEN || ! mToolbarHidingGlobal;
mIncognitoModeEnabled = prefs.getBoolean(Settings.PREF_ALWAYS_INCOGNITO_MODE, Defaults.PREF_ALWAYS_INCOGNITO_MODE) || mInputAttributes.mNoLearning
|| mInputAttributes.mIsPasswordField;
mKeyboardHeightScale = prefs.getFloat(Settings.PREF_KEYBOARD_HEIGHT_SCALE, Defaults.PREF_KEYBOARD_HEIGHT_SCALE);
mKeyboardHeightScale = Settings.readHeightScale(prefs, isLandscape);
mSpaceSwipeHorizontal = Settings.readHorizontalSpaceSwipe(prefs);
mSpaceSwipeVertical = Settings.readVerticalSpaceSwipe(prefs);
mLanguageSwipeDistance = prefs.getInt(Settings.PREF_LANGUAGE_SWIPE_DISTANCE, Defaults.PREF_LANGUAGE_SWIPE_DISTANCE);
@ -255,11 +253,11 @@ public class SettingsValues {
mClipboardHistoryEnabled = prefs.getBoolean(Settings.PREF_ENABLE_CLIPBOARD_HISTORY, Defaults.PREF_ENABLE_CLIPBOARD_HISTORY);
mClipboardHistoryRetentionTime = prefs.getInt(Settings.PREF_CLIPBOARD_HISTORY_RETENTION_TIME, Defaults.PREF_CLIPBOARD_HISTORY_RETENTION_TIME);
mOneHandedModeEnabled = Settings.readOneHandedModeEnabled(prefs, isLandscape);
mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs, isLandscape);
mOneHandedModeEnabled = Settings.readOneHandedModeEnabled(prefs, isLandscape, mIsSplitKeyboardEnabled);
mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs, isLandscape, mIsSplitKeyboardEnabled);
if (mOneHandedModeEnabled) {
final float baseScale = res.getFraction(R.fraction.config_one_handed_mode_width, 1, 1);
final float extraScale = Settings.readOneHandedModeScale(prefs, isLandscape);
final float extraScale = Settings.readOneHandedModeScale(prefs, isLandscape, mIsSplitKeyboardEnabled);
mOneHandedModeScale = 1 - (1 - baseScale) * extraScale;
} else
mOneHandedModeScale = 1f;
@ -282,7 +280,7 @@ public class SettingsValues {
);
mSpacingAndPunctuations = new SpacingAndPunctuations(res, mUrlDetectionEnabled);
mBottomPaddingScale = Settings.readBottomPaddingScale(prefs, isLandscape);
mSidePaddingScale = Settings.readSidePaddingScale(prefs, isLandscape);
mSidePaddingScale = Settings.readSidePaddingScale(prefs, isLandscape, mIsSplitKeyboardEnabled);
mLongPressSymbolsForNumpad = prefs.getBoolean(Settings.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD, Defaults.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD);
mAutoShowToolbar = mToolbarMode == ToolbarMode.EXPANDABLE && prefs.getBoolean(Settings.PREF_AUTO_SHOW_TOOLBAR, Defaults.PREF_AUTO_SHOW_TOOLBAR);
mAutoHideToolbar = mSuggestionsEnabledPerUserSettings && prefs.getBoolean(Settings.PREF_AUTO_HIDE_TOOLBAR, Defaults.PREF_AUTO_HIDE_TOOLBAR);

View file

@ -109,7 +109,8 @@ private fun DictionaryDetails(dict: File) {
DeleteButton { showDeleteDialog = true }
ExpandButton { showDetails = !showDetails }
}
AnimatedVisibility(showDetails, enter = fadeIn(), exit = fadeOut()) { // default animation looks better, but makes the dialog flash
// default animations look better but make the dialog flash, see also MultiSliderPreference
AnimatedVisibility(showDetails, enter = fadeIn(), exit = fadeOut()) {
Text(
header.info(LocalConfiguration.current.locale()),
style = MaterialTheme.typography.bodyMedium,

View file

@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.settings.preferences
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Checkbox
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
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.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import helium314.keyboard.latin.R
import helium314.keyboard.latin.settings.createPrefKeyForBooleanSettings
import helium314.keyboard.latin.utils.prefs
import helium314.keyboard.settings.Theme
import helium314.keyboard.settings.WithSmallTitle
import helium314.keyboard.settings.dialogs.ThreeButtonAlertDialog
import helium314.keyboard.settings.previewDark
// too specialized for using a more generic dialog
// actual key for each setting is baseKey with one _true/_false appended per dimension (need to keep order!)
// todo: possible adjustments, maybe depending on user feedback
// should dimension checkboxes have any other effect than just showing / hiding sliders?
// one could argue that e.g. when disabling the split checkbox, then split mode should not affect the setting
// store checkbox states?
// if so, per setting or global?
// show a description? currently commented because it could get long, even without showing the variations
// maybe if we store the checkbox state in a setting, we could use it for determining what to show
@Composable
fun MultiSliderPreference(
name: String,
baseKey: String,
dimensions: List<String>,
defaults: Array<Float>,
range: ClosedFloatingPointRange<Float>,
description: (Float) -> String,
onDone: () -> Unit
) {
if (defaults.size != 1.shl(dimensions.size))
throw ArithmeticException("defaults size does not match with dimensions, expected ${1.shl(dimensions.size)}, got ${defaults.size}")
var showDialog by remember { mutableStateOf(false) }
//val (_, keys) = remember { createVariantsAndKeys(dimensions, baseKey) }
//val prefs = LocalContext.current.prefs()
Preference(
name = name,
onClick = { showDialog = true },
//description = keys.mapIndexed { i, it -> description(prefs.getFloat(it, defaults[i])) }.joinToString(" $SPLIT ")
)
if (showDialog)
MultiSliderDialog(
onDismissRequest = { showDialog = false },
title = { Text(name) },
baseKey = baseKey,
onDone = onDone,
defaultValues = defaults,
range = range,
dimensions = dimensions,
positionString = description
)
}
// SliderDialog, but for multiple sliders with same range, each with a different setting and title
@Composable
private fun MultiSliderDialog(
onDismissRequest: () -> Unit,
title: @Composable () -> Unit,
baseKey: String,
onDone: () -> Unit,
defaultValues: Array<Float>,
range: ClosedFloatingPointRange<Float>,
dimensions: List<String>,
modifier: Modifier = Modifier,
positionString: (Float) -> String,
) {
val (variants, keys) = createVariantsAndKeys(dimensions, baseKey)
var checked by remember { mutableStateOf(List(variants.size) { true }) }
val prefs = LocalContext.current.prefs()
val done = remember { mutableMapOf<String, () -> Unit>() }
ThreeButtonAlertDialog(
onDismissRequest = onDismissRequest,
onConfirmed = { done.values.forEach { it.invoke() }; onDone() },
modifier = modifier,
title = title,
content = {
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.bodyLarge
) {
val state = rememberScrollState()
Column(Modifier.verticalScroll(state)) {
if (dimensions.size > 1) {
dimensions.forEachIndexed { i, dimension ->
DimensionCheckbox(checked[i], dimension) {
checked = checked.mapIndexed { j, c -> if (i == j) it else c }
}
}
}
variants.forEachIndexed { i, variant ->
val key = keys[i]
var sliderPosition by remember { mutableFloatStateOf(prefs.getFloat(key, defaultValues[i])) }
if (!done.contains(variant))
done[variant] = {
if (sliderPosition == defaultValues[i])
prefs.edit().remove(key).apply()
else
prefs.edit().putFloat(key, sliderPosition).apply()
}
val forbiddenDimensions = dimensions.filterIndexed { index, _ -> !checked[index] }
val visible = variant.split(SPLIT).none { it in forbiddenDimensions }
// default animations make the dialog flash (see also DictionaryDialog)
AnimatedVisibility(visible, exit = fadeOut(), enter = fadeIn()) {
WithSmallTitle(variant.ifEmpty { stringResource(R.string.button_default) }) {
Slider(
value = sliderPosition,
onValueChange = { sliderPosition = it },
valueRange = range,
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(positionString(sliderPosition))
TextButton({ sliderPosition = defaultValues[i] }) { Text(stringResource(R.string.button_default)) }
}
Spacer(Modifier.height(6.dp))
}
}
}
}
}
},
)
}
@Composable
private fun DimensionCheckbox(checked: Boolean, dimension: String, onCheckedChange: (Boolean) -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().clickable { onCheckedChange(!checked) }
) {
Checkbox(
checked = checked,
onCheckedChange = { onCheckedChange(it) }
)
Text(dimension)
}
}
private fun createVariantsAndKeys(dimensions: List<String>, baseKey: String): Pair<List<String>, List<String>> {
val variants = mutableListOf("")
val keys = mutableListOf(createPrefKeyForBooleanSettings(baseKey, 0, dimensions.size))
var i = 1
dimensions.forEach { dimension ->
variants.toList().forEach { variant ->
if (variant.isEmpty()) variants.add(dimension)
else variants.add(variant + SPLIT + dimension)
keys.add(createPrefKeyForBooleanSettings(baseKey, i, dimensions.size))
i++
}
}
return variants to keys
}
private const val SPLIT = " / "
@Preview
@Composable
private fun Preview() {
Theme(previewDark) {
MultiSliderDialog(
onDismissRequest = { },
onDone = { },
positionString = { "${it.toInt()}%"},
defaultValues = Array(8) { 100f - it % 2 * 50f },
range = 0f..500f,
title = { Text("bottom padding scale") },
dimensions = listOf("landscape", "unfolded", "split"),
baseKey = ""
)
}
}

View file

@ -37,6 +37,7 @@ import helium314.keyboard.settings.dialogs.CustomizeIconsDialog
import helium314.keyboard.settings.initPreview
import helium314.keyboard.settings.preferences.BackgroundImagePref
import helium314.keyboard.settings.preferences.CustomFontPreference
import helium314.keyboard.settings.preferences.MultiSliderPreference
import helium314.keyboard.settings.preferences.TextInputPreference
import helium314.keyboard.settings.previewDark
@ -65,18 +66,15 @@ fun AppearanceScreen(
SettingsWithoutKey.BACKGROUND_IMAGE_LANDSCAPE,
R.string.settings_category_miscellaneous,
Settings.PREF_ENABLE_SPLIT_KEYBOARD,
if (prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, Defaults.PREF_ENABLE_SPLIT_KEYBOARD))
Settings.PREF_SPLIT_SPACER_SCALE else null,
Settings.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE,
if (prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE, Defaults.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE))
Settings.PREF_SPLIT_SPACER_SCALE_LANDSCAPE else null,
if (prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE, Defaults.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE)
|| prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, Defaults.PREF_ENABLE_SPLIT_KEYBOARD))
Settings.PREF_SPLIT_SPACER_SCALE_PREFIX else null,
if (prefs.getBoolean(Settings.PREF_THEME_KEY_BORDERS, Defaults.PREF_THEME_KEY_BORDERS))
Settings.PREF_NARROW_KEY_GAPS else null,
Settings.PREF_KEYBOARD_HEIGHT_SCALE,
Settings.PREF_BOTTOM_PADDING_SCALE,
Settings.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE,
Settings.PREF_SIDE_PADDING_SCALE,
Settings.PREF_SIDE_PADDING_SCALE_LANDSCAPE,
Settings.PREF_KEYBOARD_HEIGHT_SCALE_PREFIX,
Settings.PREF_BOTTOM_PADDING_SCALE_PREFIX,
Settings.PREF_SIDE_PADDING_SCALE_PREFIX,
Settings.PREF_SPACE_BAR_TEXT,
SettingsWithoutKey.CUSTOM_FONT,
Settings.PREF_FONT_SCALE,
@ -196,11 +194,12 @@ fun createAppearanceSettings(context: Context) = listOf(
Setting(context, Settings.PREF_ENABLE_SPLIT_KEYBOARD, R.string.enable_split_keyboard) {
SwitchPreference(it, Defaults.PREF_ENABLE_SPLIT_KEYBOARD) { KeyboardSwitcher.getInstance().reloadKeyboard() }
},
Setting(context, Settings.PREF_SPLIT_SPACER_SCALE, R.string.split_spacer_scale) { setting ->
SliderPreference(
Setting(context, Settings.PREF_SPLIT_SPACER_SCALE_PREFIX, R.string.split_spacer_scale) { setting ->
MultiSliderPreference(
name = setting.title,
key = setting.key,
default = Defaults.PREF_SPLIT_SPACER_SCALE,
baseKey = setting.key,
dimensions = listOf(stringResource(R.string.landscape)),
defaults = Defaults.PREF_SPLIT_SPACER_SCALE,
range = 0.5f..2f,
description = { "${(100 * it).toInt()}%" }
) { KeyboardSwitcher.getInstance().setThemeNeedsReload() }
@ -208,59 +207,35 @@ fun createAppearanceSettings(context: Context) = listOf(
Setting(context, Settings.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE, R.string.enable_split_keyboard_landscape) {
SwitchPreference(it, Defaults.PREF_ENABLE_SPLIT_KEYBOARD_LANDSCAPE) { KeyboardSwitcher.getInstance().reloadKeyboard() }
},
Setting(context, Settings.PREF_SPLIT_SPACER_SCALE_LANDSCAPE, R.string.split_spacer_scale_landscape) { setting ->
SliderPreference(
name = setting.title,
key = setting.key,
default = Defaults.PREF_SPLIT_SPACER_SCALE_LANDSCAPE,
range = 0.5f..2f,
description = { "${(100 * it).toInt()}%" }
) { KeyboardSwitcher.getInstance().setThemeNeedsReload() }
},
Setting(context, Settings.PREF_NARROW_KEY_GAPS, R.string.prefs_narrow_key_gaps) {
SwitchPreference(it, Defaults.PREF_NARROW_KEY_GAPS) { KeyboardSwitcher.getInstance().setThemeNeedsReload() }
},
Setting(context, Settings.PREF_KEYBOARD_HEIGHT_SCALE, R.string.prefs_keyboard_height_scale) { setting ->
SliderPreference(
Setting(context, Settings.PREF_KEYBOARD_HEIGHT_SCALE_PREFIX, R.string.prefs_keyboard_height_scale) { setting ->
MultiSliderPreference(
name = setting.title,
key = setting.key,
default = Defaults.PREF_KEYBOARD_HEIGHT_SCALE,
baseKey = setting.key,
dimensions = listOf(stringResource(R.string.landscape)),
defaults = Defaults.PREF_KEYBOARD_HEIGHT_SCALE,
range = 0.3f..1.5f,
description = { "${(100 * it).toInt()}%" }
) { KeyboardSwitcher.getInstance().setThemeNeedsReload() }
},
Setting(context, Settings.PREF_BOTTOM_PADDING_SCALE, R.string.prefs_bottom_padding_scale) { setting ->
SliderPreference(
Setting(context, Settings.PREF_BOTTOM_PADDING_SCALE_PREFIX, R.string.prefs_bottom_padding_scale) { setting ->
MultiSliderPreference(
name = setting.title,
key = setting.key,
default = Defaults.PREF_BOTTOM_PADDING_SCALE,
baseKey = setting.key,
dimensions = listOf(stringResource(R.string.landscape)),
defaults = Defaults.PREF_BOTTOM_PADDING_SCALE,
range = 0f..5f,
description = { "${(100 * it).toInt()}%" }
) { KeyboardSwitcher.getInstance().setThemeNeedsReload() }
},
Setting(context, Settings.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, R.string.prefs_bottom_padding_scale_landscape) { setting ->
SliderPreference(
Setting(context, Settings.PREF_SIDE_PADDING_SCALE_PREFIX, R.string.prefs_side_padding_scale) { setting ->
MultiSliderPreference(
name = setting.title,
key = setting.key,
default = Defaults.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE,
range = 0f..5f,
description = { "${(100 * it).toInt()}%" }
) { KeyboardSwitcher.getInstance().setThemeNeedsReload() }
},
Setting(context, Settings.PREF_SIDE_PADDING_SCALE, R.string.prefs_side_padding_scale) { setting ->
SliderPreference(
name = setting.title,
key = setting.key,
default = Defaults.PREF_SIDE_PADDING_SCALE,
range = 0f..3f,
description = { "${(100 * it).toInt()}%" }
) { KeyboardSwitcher.getInstance().setThemeNeedsReload() }
},
Setting(context, Settings.PREF_SIDE_PADDING_SCALE_LANDSCAPE, R.string.prefs_side_padding_scale_landscape) { setting ->
SliderPreference(
name = setting.title,
key = setting.key,
default = Defaults.PREF_SIDE_PADDING_SCALE_LANDSCAPE,
baseKey = setting.key,
dimensions = listOf(stringResource(R.string.landscape), stringResource(R.string.split)),
defaults = Defaults.PREF_SIDE_PADDING_SCALE,
range = 0f..3f,
description = { "${(100 * it).toInt()}%" }
) { KeyboardSwitcher.getInstance().setThemeNeedsReload() }