Adjust search action settings screen design
This commit is contained in:
@@ -88,21 +88,7 @@ fun PreferenceScreen(
|
||||
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
|
||||
val touchSlop = LocalViewConfiguration.current.touchSlop
|
||||
var fabVisible by remember { mutableStateOf(true) }
|
||||
val nestedScrollConnection = remember {
|
||||
object : NestedScrollConnection {
|
||||
override fun onPostScroll(
|
||||
consumed: Offset,
|
||||
available: Offset,
|
||||
source: NestedScrollSource
|
||||
): Offset {
|
||||
if (consumed.y < -touchSlop) fabVisible = false
|
||||
else if (consumed.y > touchSlop) fabVisible = true
|
||||
return super.onPostScroll(consumed, available, source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
@@ -19,30 +19,6 @@ import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Density
|
||||
|
||||
/**
|
||||
* Animates the corners of a shape between its original value and 0.
|
||||
* For each parameter set to true, the corresponding corner will animate to the original value.
|
||||
* Otherwise, it will animate to 0.
|
||||
*/
|
||||
@Composable
|
||||
fun CornerBasedShape.animateCorners(
|
||||
topStart: Boolean,
|
||||
topEnd: Boolean,
|
||||
bottomEnd: Boolean,
|
||||
bottomStart: Boolean,
|
||||
): CornerBasedShape {
|
||||
val value by animateShapeAsState(
|
||||
copy(
|
||||
topStart = if (topStart) this.topStart else CornerSize(0f),
|
||||
topEnd = if (topEnd) this.topEnd else CornerSize(0f),
|
||||
bottomEnd = if (bottomEnd) this.bottomEnd else CornerSize(0f),
|
||||
bottomStart = if (bottomStart) this.bottomStart else CornerSize(0f)
|
||||
)
|
||||
)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate between two shapes.
|
||||
* Limitations:
|
||||
@@ -52,12 +28,19 @@ fun CornerBasedShape.animateCorners(
|
||||
*/
|
||||
@Composable
|
||||
fun animateShapeAsState(
|
||||
shape: CornerBasedShape,
|
||||
shape: Shape,
|
||||
animationSpec: AnimationSpec<CornerBasedShape> = remember { spring() },
|
||||
visibilityThreshold: CornerBasedShape? = null,
|
||||
label: String = "ValueAnimation",
|
||||
finishedListener: ((CornerBasedShape) -> Unit)? = null
|
||||
): State<CornerBasedShape> {
|
||||
finishedListener: ((Shape) -> Unit)? = null
|
||||
): State<Shape> {
|
||||
|
||||
if (shape !is CornerBasedShape) {
|
||||
return remember(shape) {
|
||||
mutableStateOf(shape)
|
||||
}
|
||||
}
|
||||
|
||||
val density = LocalDensity.current
|
||||
val converter = remember(shape.javaClass, density) {
|
||||
if (shape is CutCornerShape) CutCornerShapeConverter(density)
|
||||
|
||||
@@ -6,19 +6,22 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
import androidx.compose.material3.DropdownMenuGroup
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.DropdownMenuPopup
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -26,6 +29,7 @@ import androidx.compose.material3.MenuDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -34,8 +38,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -54,6 +61,7 @@ import de.mm20.launcher2.ui.component.dragndrop.rememberLazyDragAndDropListState
|
||||
import de.mm20.launcher2.ui.component.getSearchActionIconVector
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.ktx.animateShapeAsState
|
||||
import de.mm20.launcher2.ui.locals.LocalBackStack
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -84,6 +92,8 @@ fun SearchActionsSettingsScreen() {
|
||||
val searchActions by viewModel.searchActions.collectAsState()
|
||||
val disabledActions by viewModel.disabledActions.collectAsState()
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
CenterAlignedTopAppBar(
|
||||
@@ -95,6 +105,10 @@ fun SearchActionsSettingsScreen() {
|
||||
maxLines = 1
|
||||
)
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
if (backStack.size <= 1) {
|
||||
@@ -110,12 +124,6 @@ fun SearchActionsSettingsScreen() {
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = { viewModel.createAction() }) {
|
||||
Icon(
|
||||
painterResource(R.drawable.add_24px),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
IconButton(onClick = {
|
||||
CustomTabsIntent.Builder()
|
||||
.setDefaultColorSchemeParams(
|
||||
@@ -134,9 +142,12 @@ fun SearchActionsSettingsScreen() {
|
||||
contentDescription = "Help"
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
}) {
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||
) {
|
||||
|
||||
LazyDragAndDropColumn(
|
||||
state = listState,
|
||||
@@ -144,6 +155,8 @@ fun SearchActionsSettingsScreen() {
|
||||
contentPadding = it,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
item(
|
||||
key = "disabled-info"
|
||||
@@ -168,19 +181,28 @@ fun SearchActionsSettingsScreen() {
|
||||
)
|
||||
}
|
||||
}
|
||||
items(
|
||||
itemsIndexed(
|
||||
items = searchActions,
|
||||
key = { it.key }
|
||||
) { item ->
|
||||
key = { _, it -> it.key }
|
||||
) { index, item ->
|
||||
DraggableItem(
|
||||
state = listState,
|
||||
key = item.key
|
||||
) {
|
||||
|
||||
val shape = getShape(index, searchActions.size)
|
||||
val draggedShape = MaterialTheme.shapes.medium
|
||||
|
||||
val elevation by animateDpAsState(if (it) 4.dp else 0.dp)
|
||||
Surface(
|
||||
shadowElevation = elevation,
|
||||
tonalElevation = elevation,
|
||||
modifier = Modifier.zIndex(if (it) 1f else 0f)
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
.zIndex(if (it) 1f else 0f),
|
||||
shape = animateShapeAsState(
|
||||
if (it) draggedShape else shape
|
||||
).value
|
||||
) {
|
||||
if (item is CustomizableSearchActionBuilder) {
|
||||
Preference(
|
||||
@@ -263,24 +285,48 @@ fun SearchActionsSettingsScreen() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(0.5.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
|
||||
)
|
||||
.height(8.dp)
|
||||
)
|
||||
}
|
||||
items(
|
||||
itemsIndexed(
|
||||
items = disabledActions,
|
||||
key = { "disabled-${it.key}" }
|
||||
) { item ->
|
||||
SwitchPreference(
|
||||
icon = getSearchActionIconVector(item.icon),
|
||||
title = item.label,
|
||||
value = false,
|
||||
onValueChanged = {
|
||||
viewModel.addAction(item)
|
||||
}
|
||||
)
|
||||
key = { _, it -> "disabled-${it.key}" }
|
||||
) { index, item ->
|
||||
val shape = getShape(index, disabledActions.size)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
.clip(shape)
|
||||
) {
|
||||
SwitchPreference(
|
||||
icon = getSearchActionIconVector(item.icon),
|
||||
title = item.label,
|
||||
value = false,
|
||||
onValueChanged = {
|
||||
viewModel.addAction(item)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
item(key = "disabled-button") {
|
||||
FilledTonalButton(
|
||||
modifier = Modifier
|
||||
.padding(top = 10.dp, start = 12.dp, end = 12.dp)
|
||||
.navigationBarsPadding(),
|
||||
contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
|
||||
onClick = {
|
||||
viewModel.createAction()
|
||||
}) {
|
||||
Icon(
|
||||
painterResource(R.drawable.add_20px),
|
||||
null,
|
||||
modifier = Modifier
|
||||
.padding(end = ButtonDefaults.IconSpacing)
|
||||
.size(ButtonDefaults.IconSize)
|
||||
)
|
||||
Text(stringResource(R.string.create_search_action_title))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,4 +354,30 @@ fun SearchActionsSettingsScreen() {
|
||||
viewModel.dismissDialogs()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getShape(index: Int, total: Int): Shape {
|
||||
if (total == 1) {
|
||||
return MaterialTheme.shapes.medium
|
||||
}
|
||||
|
||||
if (total > 1 && index > 0 && index < total - 1) {
|
||||
return MaterialTheme.shapes.extraSmall
|
||||
}
|
||||
|
||||
val xs = MaterialTheme.shapes.extraSmall
|
||||
val md = MaterialTheme.shapes.medium
|
||||
|
||||
if (index == 0) {
|
||||
return xs.copy(
|
||||
topStart = md.topStart,
|
||||
topEnd = md.topEnd
|
||||
)
|
||||
} else {
|
||||
return xs.copy(
|
||||
bottomStart = md.bottomStart,
|
||||
bottomEnd = md.bottomEnd
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user