Drop items on tags in edit favorites sheet to tag them
This commit is contained in:
parent
2c307693f2
commit
60990e767c
@ -32,6 +32,7 @@ interface CustomAttributesRepository {
|
|||||||
|
|
||||||
suspend fun getAllTags(startsWith: String? = null): List<String>
|
suspend fun getAllTags(startsWith: String? = null): List<String>
|
||||||
fun getItemsForTag(tag: String): Flow<List<Searchable>>
|
fun getItemsForTag(tag: String): Flow<List<Searchable>>
|
||||||
|
fun addTag(item: Searchable, tag: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class CustomAttributesRepositoryImpl(
|
internal class CustomAttributesRepositoryImpl(
|
||||||
@ -122,6 +123,13 @@ internal class CustomAttributesRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addTag(item: Searchable, tag: String) {
|
||||||
|
val dao = appDatabase.customAttrsDao()
|
||||||
|
scope.launch {
|
||||||
|
dao.addTag(item.key, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun search(query: String): Flow<List<Searchable>> {
|
override suspend fun search(query: String): Flow<List<Searchable>> {
|
||||||
if (query.isBlank()) {
|
if (query.isBlank()) {
|
||||||
return flow {
|
return flow {
|
||||||
|
|||||||
@ -41,4 +41,17 @@ interface CustomAttrsDao {
|
|||||||
|
|
||||||
@Query("SELECT key FROM CustomAttributes WHERE type = 'tag' AND value = :tag")
|
@Query("SELECT key FROM CustomAttributes WHERE type = 'tag' AND value = :tag")
|
||||||
fun getItemsWithTag(tag: String): Flow<List<String>>
|
fun getItemsWithTag(tag: String): Flow<List<String>>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
suspend fun addTag(key: String, tag: String) {
|
||||||
|
removeTag(key, tag)
|
||||||
|
insertTag(key, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query("DELETE FROM CustomAttributes WHERE type = 'tag' AND key = :key AND value = :tag")
|
||||||
|
suspend fun removeTag(key: String, tag: String)
|
||||||
|
|
||||||
|
@Query("INSERT INTO CustomAttributes (key, value, type) VALUES (:key, :tag, 'tag')")
|
||||||
|
suspend fun insertTag(key: String, tag: String)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ import kotlin.coroutines.coroutineContext
|
|||||||
fun rememberLazyDragAndDropGridState(
|
fun rememberLazyDragAndDropGridState(
|
||||||
gridState: LazyGridState = rememberLazyGridState(),
|
gridState: LazyGridState = rememberLazyGridState(),
|
||||||
onDragStart: (item: LazyGridItemInfo) -> Boolean = { true },
|
onDragStart: (item: LazyGridItemInfo) -> Boolean = { true },
|
||||||
onDrag: (item: LazyGridItemInfo, offset: Offset) -> Unit = {_, _ ->},
|
onDrag: (item: LazyGridItemInfo, offset: Offset, position: Offset) -> Unit = {_, _, _ ->},
|
||||||
onDragEnd: (item: LazyGridItemInfo) -> Unit = {},
|
onDragEnd: (item: LazyGridItemInfo) -> Unit = {},
|
||||||
onDragCancel: (item: LazyGridItemInfo) -> Unit = {},
|
onDragCancel: (item: LazyGridItemInfo) -> Unit = {},
|
||||||
onItemMove: (from: LazyGridItemInfo, to: LazyGridItemInfo) -> Unit
|
onItemMove: (from: LazyGridItemInfo, to: LazyGridItemInfo) -> Unit
|
||||||
@ -51,7 +51,7 @@ fun rememberLazyDragAndDropGridState(
|
|||||||
data class LazyDragAndDropGridState(
|
data class LazyDragAndDropGridState(
|
||||||
val gridState: LazyGridState,
|
val gridState: LazyGridState,
|
||||||
val onDragStart: (item: LazyGridItemInfo) -> Boolean = { true },
|
val onDragStart: (item: LazyGridItemInfo) -> Boolean = { true },
|
||||||
val onDrag: (item: LazyGridItemInfo, offset: Offset) -> Unit = {_, _ ->},
|
val onDrag: (item: LazyGridItemInfo, offset: Offset, position: Offset) -> Unit = {_, _, _ ->},
|
||||||
val onDragEnd: (item: LazyGridItemInfo) -> Unit = {},
|
val onDragEnd: (item: LazyGridItemInfo) -> Unit = {},
|
||||||
val onDragCancel: (item: LazyGridItemInfo) -> Unit = {},
|
val onDragCancel: (item: LazyGridItemInfo) -> Unit = {},
|
||||||
val onItemMove: (from: LazyGridItemInfo, to: LazyGridItemInfo) -> Unit
|
val onItemMove: (from: LazyGridItemInfo, to: LazyGridItemInfo) -> Unit
|
||||||
@ -284,7 +284,7 @@ fun Modifier.dragAndDrop(
|
|||||||
state.endScrolling()
|
state.endScrolling()
|
||||||
}
|
}
|
||||||
|
|
||||||
state.draggedItemOffset?.let { state.onDrag(draggedItem, it) }
|
state.draggedItemOffset?.let { state.onDrag(draggedItem, it, absPosition) }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDragCancel = {
|
onDragCancel = {
|
||||||
|
|||||||
@ -9,11 +9,17 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||||
|
import androidx.compose.foundation.hoverable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.interaction.collectIsHoveredAsState
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||||
|
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.*
|
import androidx.compose.material.icons.rounded.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
@ -22,9 +28,11 @@ import androidx.compose.runtime.livedata.observeAsState
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.drawBehind
|
import androidx.compose.ui.draw.drawBehind
|
||||||
|
import androidx.compose.ui.geometry.Rect
|
||||||
import androidx.compose.ui.graphics.PathEffect
|
import androidx.compose.ui.graphics.PathEffect
|
||||||
import androidx.compose.ui.graphics.drawOutline
|
import androidx.compose.ui.graphics.drawOutline
|
||||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@ -32,6 +40,8 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.toOffset
|
||||||
|
import androidx.compose.ui.unit.toSize
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.badges.Badge
|
import de.mm20.launcher2.badges.Badge
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
@ -43,6 +53,8 @@ import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
|||||||
import de.mm20.launcher2.ui.ktx.toPixels
|
import de.mm20.launcher2.ui.ktx.toPixels
|
||||||
import de.mm20.launcher2.ui.launcher.helper.*
|
import de.mm20.launcher2.ui.launcher.helper.*
|
||||||
import de.mm20.launcher2.ui.locals.LocalGridColumns
|
import de.mm20.launcher2.ui.locals.LocalGridColumns
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -119,19 +131,55 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM) {
|
|||||||
|
|
||||||
val contextMenuCloseDistance = 8.dp.toPixels()
|
val contextMenuCloseDistance = 8.dp.toPixels()
|
||||||
|
|
||||||
|
var draggedItemKey by remember { mutableStateOf<String?>(null) }
|
||||||
|
var hoveredTag by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
val gridState = rememberLazyGridState()
|
||||||
|
val tagsListState = rememberLazyListState()
|
||||||
|
val tagsTitleSize = 48.dp.toPixels()
|
||||||
val state = rememberLazyDragAndDropGridState(
|
val state = rememberLazyDragAndDropGridState(
|
||||||
|
gridState = gridState,
|
||||||
onDragStart = {
|
onDragStart = {
|
||||||
val item = items.getOrNull(it.index)
|
val item = items.getOrNull(it.index)
|
||||||
|
|
||||||
if (item !is FavoritesSheetGridItem.Favorite) return@rememberLazyDragAndDropGridState false
|
if (item !is FavoritesSheetGridItem.Favorite) return@rememberLazyDragAndDropGridState false
|
||||||
|
|
||||||
|
draggedItemKey = item.item.key
|
||||||
contextMenuItemKey = item.item.key
|
contextMenuItemKey = item.item.key
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
onDrag = { _, offset ->
|
onDrag = { item, offset, position ->
|
||||||
if (offset.getDistanceSquared() > contextMenuCloseDistance) {
|
if (offset.getDistanceSquared() > contextMenuCloseDistance) {
|
||||||
contextMenuItemKey = null
|
contextMenuItemKey = null
|
||||||
}
|
}
|
||||||
|
val draggedCenter = Rect(position, item.size.toSize()).center
|
||||||
|
val hoveredItem = gridState.layoutInfo.visibleItemsInfo.find {
|
||||||
|
Rect(
|
||||||
|
it.offset.toOffset(),
|
||||||
|
it.size.toSize()
|
||||||
|
).contains(draggedCenter)
|
||||||
|
}
|
||||||
|
if (hoveredItem != null
|
||||||
|
&& items[hoveredItem.index] is FavoritesSheetGridItem.Tags
|
||||||
|
&& hoveredItem.offset.y + tagsTitleSize < position.y
|
||||||
|
) {
|
||||||
|
val scroll = tagsListState.layoutInfo.viewportStartOffset
|
||||||
|
val tag = tagsListState.layoutInfo.visibleItemsInfo.find {
|
||||||
|
position.x + scroll > it.offset && position.x + scroll < it.offset + it.size
|
||||||
|
}
|
||||||
|
hoveredTag = tag?.index?.let { pinnedTags[it].tag }
|
||||||
|
} else {
|
||||||
|
hoveredTag = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDragEnd = {
|
||||||
|
viewModel.addTag(draggedItemKey, hoveredTag)
|
||||||
|
draggedItemKey = null
|
||||||
|
hoveredTag = null
|
||||||
|
},
|
||||||
|
onDragCancel = {
|
||||||
|
draggedItemKey = null
|
||||||
|
hoveredTag = null
|
||||||
}
|
}
|
||||||
) { from, to ->
|
) { from, to ->
|
||||||
viewModel.moveItem(from, to)
|
viewModel.moveItem(from, to)
|
||||||
@ -428,10 +476,15 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
if (pinnedTags.isNotEmpty()) {
|
if (pinnedTags.isNotEmpty()) {
|
||||||
val rowState = rememberLazyDragAndDropListState { from, to ->
|
val rowState = rememberLazyDragAndDropListState(
|
||||||
|
listState = tagsListState,
|
||||||
|
) { from, to ->
|
||||||
viewModel.moveTag(from, to)
|
viewModel.moveTag(from, to)
|
||||||
}
|
}
|
||||||
LazyDragAndDropRow(state = rowState) {
|
LazyDragAndDropRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
state = rowState
|
||||||
|
) {
|
||||||
items(
|
items(
|
||||||
pinnedTags,
|
pinnedTags,
|
||||||
key = { it.key }
|
key = { it.key }
|
||||||
@ -439,8 +492,15 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM) {
|
|||||||
DraggableItem(state = rowState, key = tag.key) { dragged ->
|
DraggableItem(state = rowState, key = tag.key) { dragged ->
|
||||||
|
|
||||||
FilterChip(
|
FilterChip(
|
||||||
modifier = Modifier.padding(end = 12.dp),
|
modifier = Modifier
|
||||||
selected = false,
|
.padding(end = 12.dp)
|
||||||
|
.pointerInput(null) {
|
||||||
|
val coroutineContext =
|
||||||
|
currentCoroutineContext()
|
||||||
|
|
||||||
|
}
|
||||||
|
,
|
||||||
|
selected = tag.tag == hoveredTag,
|
||||||
onClick = {},
|
onClick = {},
|
||||||
label = { Text(tag.label) },
|
label = { Text(tag.label) },
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
|
|||||||
@ -296,4 +296,14 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
|
|||||||
save()
|
save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addTag(key: String?, tag: String?) {
|
||||||
|
val gridItems = gridItems.value?.toMutableList() ?: return
|
||||||
|
if (key == null || tag == null) return
|
||||||
|
val item =
|
||||||
|
gridItems.find { it is FavoritesSheetGridItem.Favorite && it.item.key == key } as FavoritesSheetGridItem.Favorite?
|
||||||
|
if (item != null) {
|
||||||
|
customAttributesRepository.addTag(item.item, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user