mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-05-14 14:02:44 +00:00
add generic reorder dialog and prelimiary toolbar pref screen
This commit is contained in:
parent
d61963453f
commit
ce37888985
9 changed files with 203 additions and 14 deletions
|
@ -114,6 +114,7 @@ dependencies {
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||||
implementation("androidx.navigation:navigation-compose:2.8.5")
|
implementation("androidx.navigation:navigation-compose:2.8.5")
|
||||||
|
implementation("sh.calvin.reorderable:reorderable:2.4.2")
|
||||||
|
|
||||||
// color picker for user-defined colors
|
// color picker for user-defined colors
|
||||||
implementation("com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0")
|
implementation("com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0")
|
||||||
|
|
|
@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
|
||||||
import helium314.keyboard.settings.screens.createAboutPrefs
|
import helium314.keyboard.settings.screens.createAboutPrefs
|
||||||
import helium314.keyboard.settings.screens.createCorrectionPrefs
|
import helium314.keyboard.settings.screens.createCorrectionPrefs
|
||||||
import helium314.keyboard.settings.screens.createPreferencesPrefs
|
import helium314.keyboard.settings.screens.createPreferencesPrefs
|
||||||
|
import helium314.keyboard.settings.screens.createToolbarPrefs
|
||||||
|
|
||||||
class AllPrefs(context: Context) {
|
class AllPrefs(context: Context) {
|
||||||
private val list = createPrefDefs(context)
|
private val list = createPrefDefs(context)
|
||||||
|
@ -51,7 +52,7 @@ class PrefDef(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPrefDefs(context: Context) = createAboutPrefs(context) +
|
private fun createPrefDefs(context: Context) = createAboutPrefs(context) +
|
||||||
createCorrectionPrefs(context) + createPreferencesPrefs(context)
|
createCorrectionPrefs(context) + createPreferencesPrefs(context) + createToolbarPrefs(context)
|
||||||
|
|
||||||
// todo: move somewhere else
|
// todo: move somewhere else
|
||||||
fun Context.getActivity(): ComponentActivity? {
|
fun Context.getActivity(): ComponentActivity? {
|
||||||
|
|
|
@ -14,10 +14,13 @@ import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
// todo
|
// todo
|
||||||
// add reorder / enable dialog (maybe not as dialog?)
|
// add reorder / enable dialog
|
||||||
|
// reorder part is already done
|
||||||
// more pref screens, and other super-custom things
|
// more pref screens, and other super-custom things
|
||||||
// consider IME insets when searching
|
// consider IME insets when searching
|
||||||
// improve performance when loading screens with many settings (lazyColumn?)
|
// improve performance when loading screens with many settings (lazyColumn?)
|
||||||
|
// screens could have a lazy column of preferences and category separators, and the list has an if-setting-then-null for hiding
|
||||||
|
// lazyColumn also has the key, this should be used! and must be unique
|
||||||
// consider that stuff in composables can get called quite often on any changes -> use remember for things that are slow (maybe add test logging)
|
// consider that stuff in composables can get called quite often on any changes -> use remember for things that are slow (maybe add test logging)
|
||||||
|
|
||||||
// later
|
// later
|
||||||
|
@ -29,7 +32,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
// rename some classes
|
// rename some classes
|
||||||
// split the preferences in allPrefs.createDefs into multiple files, this will get horribly long
|
// split the preferences in allPrefs.createDefs into multiple files, this will get horribly long
|
||||||
// maybe have sub-lists in the pref screens using the settings?
|
// maybe have sub-lists in the pref screens using the settings?
|
||||||
// spdx headers everywhere
|
// spdx headers everywhere (except DragDropColumn, which is from stackoverflow without explicit license)
|
||||||
// changes to anything but the compose settings package should not be in the initial PR
|
// changes to anything but the compose settings package should not be in the initial PR
|
||||||
// commit them separately if possible
|
// commit them separately if possible
|
||||||
// though some might be necessary
|
// though some might be necessary
|
||||||
|
@ -43,6 +46,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
// show as disabled -> users confused
|
// show as disabled -> users confused
|
||||||
// show (but change will not do anything because another setting needs to be enabled first)
|
// show (but change will not do anything because another setting needs to be enabled first)
|
||||||
// -> users confused, but probably better than the 2 above
|
// -> users confused, but probably better than the 2 above
|
||||||
|
// adjust layout a little, there is too much empty space
|
||||||
|
|
||||||
// maybe later
|
// maybe later
|
||||||
// weird problem with app sometimes closing on back, but that's related to "old" settings (don't care if all are removed)
|
// weird problem with app sometimes closing on back, but that's related to "old" settings (don't care if all are removed)
|
||||||
|
@ -69,6 +73,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
// another 300 kb when switching material2 to material3
|
// another 300 kb when switching material2 to material3
|
||||||
// ca 150 kb reduction when removing androidx.preference
|
// ca 150 kb reduction when removing androidx.preference
|
||||||
// -> too much, but still ok if we can get nicer preference stuff
|
// -> too much, but still ok if we can get nicer preference stuff
|
||||||
|
// meh, and using a TextField adds another 300 kb... huge chunks for sth that seems so small
|
||||||
|
|
||||||
class SettingsActivity2 : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
class SettingsActivity2 : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
private val prefs by lazy { DeviceProtectedUtils.getSharedPreferences(this) }
|
private val prefs by lazy { DeviceProtectedUtils.getSharedPreferences(this) }
|
||||||
|
|
|
@ -14,6 +14,7 @@ import helium314.keyboard.settings.screens.AboutScreen
|
||||||
import helium314.keyboard.settings.screens.MainSettingsScreen
|
import helium314.keyboard.settings.screens.MainSettingsScreen
|
||||||
import helium314.keyboard.settings.screens.PreferencesScreen
|
import helium314.keyboard.settings.screens.PreferencesScreen
|
||||||
import helium314.keyboard.settings.screens.TextCorrectionScreen
|
import helium314.keyboard.settings.screens.TextCorrectionScreen
|
||||||
|
import helium314.keyboard.settings.screens.ToolbarScreen
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
@ -48,6 +49,7 @@ fun SettingsNavHost(
|
||||||
onClickAbout = { navController.navigate(SettingsDestination.About) },
|
onClickAbout = { navController.navigate(SettingsDestination.About) },
|
||||||
onClickTextCorrection = { navController.navigate(SettingsDestination.TextCorrection) },
|
onClickTextCorrection = { navController.navigate(SettingsDestination.TextCorrection) },
|
||||||
onClickPreferences = { navController.navigate(SettingsDestination.Preferences) },
|
onClickPreferences = { navController.navigate(SettingsDestination.Preferences) },
|
||||||
|
onClickToolbar = { navController.navigate(SettingsDestination.Toolbar) },
|
||||||
onClickBack = ::goBack,
|
onClickBack = ::goBack,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -66,6 +68,11 @@ fun SettingsNavHost(
|
||||||
onClickBack = ::goBack
|
onClickBack = ::goBack
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
composable(SettingsDestination.Toolbar) {
|
||||||
|
ToolbarScreen (
|
||||||
|
onClickBack = ::goBack
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +81,7 @@ object SettingsDestination {
|
||||||
const val About = "about"
|
const val About = "about"
|
||||||
const val TextCorrection = "text_correction"
|
const val TextCorrection = "text_correction"
|
||||||
const val Preferences = "preferences"
|
const val Preferences = "preferences"
|
||||||
|
const val Toolbar = "toolbar"
|
||||||
val navTarget = MutableStateFlow(Settings)
|
val navTarget = MutableStateFlow(Settings)
|
||||||
|
|
||||||
private val navScope = CoroutineScope(Dispatchers.Default)
|
private val navScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
package helium314.keyboard.settings.dialogs
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import helium314.keyboard.latin.R
|
||||||
|
import sh.calvin.reorderable.ReorderableItem
|
||||||
|
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T: Any> ReorderDialog(
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
onConfirmed: (List<T>) -> Unit,
|
||||||
|
items: List<T>,
|
||||||
|
getKey: (T) -> Any, // actually it's not "Any", but "anything that can be stored in a bundle"
|
||||||
|
displayItem: @Composable (T) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
title: @Composable (() -> Unit)? = null,
|
||||||
|
confirmButtonText: String = stringResource(android.R.string.ok),
|
||||||
|
cancelButtonText: String = stringResource(android.R.string.cancel),
|
||||||
|
shape: Shape = MaterialTheme.shapes.medium,
|
||||||
|
backgroundColor: Color = MaterialTheme.colorScheme.surface,
|
||||||
|
contentColor: Color = contentColorFor(backgroundColor),
|
||||||
|
properties: DialogProperties = DialogProperties(),
|
||||||
|
) {
|
||||||
|
var reorderableItems by remember(items) { mutableStateOf(items) }
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
val dragDropState = rememberReorderableLazyListState(listState) { from, to ->
|
||||||
|
reorderableItems = reorderableItems.toMutableList().apply {
|
||||||
|
add(to.index, removeAt(from.index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { onConfirmed(reorderableItems); onDismissRequest() }) { Text(confirmButtonText) }
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
dismissButton = { TextButton(onClick = onDismissRequest) { Text(cancelButtonText) } },
|
||||||
|
title = title,
|
||||||
|
text = {
|
||||||
|
LazyColumn(
|
||||||
|
state = listState,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
) {
|
||||||
|
items(reorderableItems, key = getKey) { item ->
|
||||||
|
ReorderableItem(
|
||||||
|
state = dragDropState,
|
||||||
|
key = getKey(item)
|
||||||
|
) { dragging ->
|
||||||
|
val elevation by animateDpAsState(if (dragging) 4.dp else 0.dp)
|
||||||
|
Surface(shadowElevation = elevation) {
|
||||||
|
Row(modifier = Modifier.longPressDraggableHandle()) {
|
||||||
|
Icon(painterResource(R.drawable.ic_drag_indicator), "Reorder")
|
||||||
|
displayItem(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = shape,
|
||||||
|
containerColor = backgroundColor,
|
||||||
|
textContentColor = contentColor,
|
||||||
|
properties = properties,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun Preview() {
|
||||||
|
ReorderDialog(
|
||||||
|
onConfirmed = {},
|
||||||
|
onDismissRequest = {},
|
||||||
|
items = listOf(1, 2, 3),
|
||||||
|
displayItem = { Text(it.toString(), Modifier.fillMaxWidth(), textAlign = TextAlign.Center) },
|
||||||
|
getKey = { it.toString() }
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
package helium314.keyboard.settings.dialogs
|
package helium314.keyboard.settings.dialogs
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
@ -23,7 +21,6 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import helium314.keyboard.latin.R
|
import helium314.keyboard.latin.R
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ fun MainSettingsScreen(
|
||||||
onClickAbout: () -> Unit,
|
onClickAbout: () -> Unit,
|
||||||
onClickTextCorrection: () -> Unit,
|
onClickTextCorrection: () -> Unit,
|
||||||
onClickPreferences: () -> Unit,
|
onClickPreferences: () -> Unit,
|
||||||
|
onClickToolbar: () -> Unit,
|
||||||
onClickBack: () -> Unit,
|
onClickBack: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val ctx = LocalContext.current
|
val ctx = LocalContext.current
|
||||||
|
@ -54,6 +55,17 @@ fun MainSettingsScreen(
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Preference(
|
||||||
|
name = stringResource(R.string.settings_screen_toolbar),
|
||||||
|
onClick = onClickToolbar,
|
||||||
|
icon = R.drawable.ic_settings_toolbar_foreground
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_arrow_left),
|
||||||
|
modifier = Modifier.scale(-1f, 1f),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
Preference(
|
Preference(
|
||||||
name = stringResource(R.string.settings_screen_correction),
|
name = stringResource(R.string.settings_screen_correction),
|
||||||
onClick = onClickTextCorrection,
|
onClick = onClickTextCorrection,
|
||||||
|
@ -129,7 +141,7 @@ fun Activity.switchTo(fragment: androidx.fragment.app.Fragment) {
|
||||||
private fun PreviewScreen() {
|
private fun PreviewScreen() {
|
||||||
Theme(true) {
|
Theme(true) {
|
||||||
Surface {
|
Surface {
|
||||||
MainSettingsScreen({}, {}, {}, {})
|
MainSettingsScreen({}, {}, {}, {}, {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,25 +4,18 @@ import android.content.Context
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import helium314.keyboard.latin.AudioAndHapticFeedbackManager
|
import helium314.keyboard.latin.AudioAndHapticFeedbackManager
|
||||||
import helium314.keyboard.latin.R
|
import helium314.keyboard.latin.R
|
||||||
import helium314.keyboard.latin.settings.Settings
|
import helium314.keyboard.latin.settings.Settings
|
||||||
import helium314.keyboard.latin.utils.DeviceProtectedUtils
|
|
||||||
import helium314.keyboard.settings.AllPrefs
|
import helium314.keyboard.settings.AllPrefs
|
||||||
import helium314.keyboard.settings.PrefDef
|
import helium314.keyboard.settings.PrefDef
|
||||||
import helium314.keyboard.settings.Preference
|
|
||||||
import helium314.keyboard.settings.SearchPrefScreen
|
import helium314.keyboard.settings.SearchPrefScreen
|
||||||
import helium314.keyboard.settings.SettingsActivity2
|
import helium314.keyboard.settings.SettingsActivity2
|
||||||
import helium314.keyboard.settings.SliderPreference
|
import helium314.keyboard.settings.SliderPreference
|
||||||
import helium314.keyboard.settings.Theme
|
import helium314.keyboard.settings.Theme
|
||||||
import helium314.keyboard.settings.dialogs.SliderDialog
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PreferencesScreen(
|
fun PreferencesScreen(
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package helium314.keyboard.settings.screens
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import helium314.keyboard.latin.R
|
||||||
|
import helium314.keyboard.latin.settings.Settings
|
||||||
|
import helium314.keyboard.settings.AllPrefs
|
||||||
|
import helium314.keyboard.settings.PrefDef
|
||||||
|
import helium314.keyboard.settings.Preference
|
||||||
|
import helium314.keyboard.settings.SearchPrefScreen
|
||||||
|
import helium314.keyboard.settings.SettingsActivity2
|
||||||
|
import helium314.keyboard.settings.Theme
|
||||||
|
import helium314.keyboard.settings.dialogs.ReorderDialog
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ToolbarScreen(
|
||||||
|
onClickBack: () -> Unit,
|
||||||
|
) {
|
||||||
|
SearchPrefScreen(
|
||||||
|
onClickBack = onClickBack,
|
||||||
|
title = stringResource(R.string.settings_screen_toolbar),
|
||||||
|
) {
|
||||||
|
SettingsActivity2.allPrefs.map[Settings.PREF_PINNED_TOOLBAR_KEYS]!!.Preference()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createToolbarPrefs(context: Context) = listOf(
|
||||||
|
PrefDef(context, Settings.PREF_PINNED_TOOLBAR_KEYS, R.string.pinned_toolbar_keys) { def ->
|
||||||
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
Preference(
|
||||||
|
name = def.title,
|
||||||
|
onClick = { showDialog = true },
|
||||||
|
)
|
||||||
|
if (showDialog) {
|
||||||
|
ReorderDialog(
|
||||||
|
onConfirmed = { },
|
||||||
|
onDismissRequest = { showDialog = false },
|
||||||
|
items = (1..40).toList(),
|
||||||
|
displayItem = { Text(it.toString(), Modifier.fillMaxWidth(), textAlign = TextAlign.Center) },
|
||||||
|
getKey = { it.hashCode() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun Preview() {
|
||||||
|
SettingsActivity2.allPrefs = AllPrefs(LocalContext.current)
|
||||||
|
Theme(true) {
|
||||||
|
Surface {
|
||||||
|
ToolbarScreen { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue