Replaced compose dropdown with a custom implementation based on context menu

This commit is contained in:
Abdelilah El Aissaoui 2023-03-25 14:07:38 +01:00
parent 37348a5dfc
commit f9ccf87030
No known key found for this signature in database
GPG Key ID: 7587FC860F594869
8 changed files with 104 additions and 39 deletions

View File

@ -28,6 +28,7 @@ object AppIcons {
const val LOCK = "lock.svg"
const val LOGO = "logo.svg"
const val MERGE = "merge.svg"
const val MESSAGE = "message.svg"
const val MORE_VERT = "more_vert.svg"
const val OPEN = "open.svg"
const val PERSON = "person.svg"

View File

@ -7,7 +7,10 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
@ -192,7 +195,7 @@ fun ExtendedMenuButton(
title: String,
icon: Painter,
onClick: () -> Unit,
extendedListItems: List<DropDownContentData>,
extendedListItems: List<ContextMenuElement>,
) {
var showDropDownMenu by remember { mutableStateOf(false) }
@ -229,31 +232,36 @@ fun ExtendedMenuButton(
)
}
Box(
modifier = Modifier
.fillMaxHeight()
.ignoreKeyEvents(),
contentAlignment = Alignment.Center,
DropdownMenu(
items = { extendedListItems }
) {
Box(
modifier = Modifier
.fillMaxHeight()
.ignoreKeyEvents(),
contentAlignment = Alignment.Center,
) {
Icon(
painterResource(AppIcons.EXPAND_MORE),
contentDescription = null,
tint = MaterialTheme.colors.onBackground,
modifier = Modifier.size(16.dp)
)
Icon(
painterResource(AppIcons.EXPAND_MORE),
contentDescription = null,
tint = MaterialTheme.colors.onBackground,
modifier = Modifier.size(16.dp)
)
DropdownMenu(
onDismissRequest = {
showDropDownMenu = false
},
content = {
for (item in extendedListItems) {
DropDownContent(item, onDismiss = { showDropDownMenu = false })
}
},
expanded = showDropDownMenu,
)
}
// DropdownMenu(
// onDismissRequest = {
// showDropDownMenu = false
// },
// content = {
// for (item in extendedListItems) {
// DropDownContent(item, onDismiss = { showDropDownMenu = false })
// }
// },
// expanded = showDropDownMenu,
// )
}
}
}

View File

@ -321,7 +321,8 @@ fun LazyListScope.submodules(
items(submodules, key = { it.first }) { submodule ->
Submodule(
submodule,
onInitializeModule = { submodulesViewModel.initializeSubmodule(submodule.first) }
onInitializeModule = { submodulesViewModel.initializeSubmodule(submodule.first) },
onOpenSubmoduleInTab = { submodulesViewModel.onOpenSubmoduleInTab(submodule.first) },
)
}
}
@ -464,12 +465,14 @@ private fun Stash(
private fun Submodule(
submodulePair: Pair<String, SubmoduleStatus>,
onInitializeModule: () -> Unit,
onOpenSubmoduleInTab: () -> Unit,
) {
ContextMenu(
items = {
submoduleContextMenuItems(
submodulePair.second,
onInitializeModule = onInitializeModule
onInitializeModule = onInitializeModule,
onOpenSubmoduleInTab = onOpenSubmoduleInTab,
)
}
) {

View File

@ -13,9 +13,11 @@ import androidx.compose.ui.awt.awtEventOrNull
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.unit.*
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
@ -36,6 +38,13 @@ fun ContextMenu(items: () -> List<ContextMenuElement>, function: @Composable ()
}
}
@Composable
fun DropdownMenu(items: () -> List<ContextMenuElement>, function: @Composable () -> Unit) {
Box(modifier = Modifier.dropdownMenu(items), propagateMinConstraints = true) {
function()
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun Modifier.contextMenu(items: () -> List<ContextMenuElement>): Modifier {
@ -66,6 +75,51 @@ private fun Modifier.contextMenu(items: () -> List<ContextMenuElement>): Modifie
lastMouseEventState.x,
lastMouseEventState.y,
items(),
onDismissRequest = { setLastMouseEventState(null) }
)
}
return mod
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun Modifier.dropdownMenu(items: () -> List<ContextMenuElement>): Modifier {
val (lastMouseEventState, setLastMouseEventState) = remember { mutableStateOf<MouseEvent?>(null) }
val (offset, setOffset) = remember { mutableStateOf<Offset?>(null) }
val mod = this
.onGloballyPositioned { layoutCoordinates ->
val offsetToRoot = layoutCoordinates.localToRoot(Offset.Zero)
println(offsetToRoot)
val offsetToBottomOfComponent = offsetToRoot.copy(y = offsetToRoot.y + layoutCoordinates.size.height)
setOffset(offsetToBottomOfComponent)
}
.pointerInput(Unit) {
while (true) {
val lastMouseEvent = awaitPointerEventScope { awaitFirstDownEvent() }
val mouseEvent = lastMouseEvent.awtEventOrNull
if (mouseEvent != null) {
if (lastMouseEvent.button.isPrimary) {
val currentCheck = System.currentTimeMillis()
if (lastCheck != 0L && currentCheck - lastCheck < MIN_TIME_BETWEEN_POPUPS) {
println("IGNORE POPUP TRIGGERED!")
} else {
lastCheck = currentCheck
setLastMouseEventState(mouseEvent)
}
}
}
}
}
if (offset != null && lastMouseEventState != null) {
showPopup(
offset.x.toInt(),
offset.y.toInt(),
items(),
onDismissRequest = { setLastMouseEventState(null) })
}

View File

@ -4,7 +4,7 @@ fun pullContextMenuItems(
onPullWith: () -> Unit,
onFetchAll: () -> Unit,
isPullWithRebaseDefault: Boolean,
): List<DropDownContentData> {
): List<ContextMenuElement> {
val pullWithText = if (isPullWithRebaseDefault) {
"Pull with merge"
} else {
@ -12,11 +12,11 @@ fun pullContextMenuItems(
}
return mutableListOf(
DropDownContentData(
ContextMenuElement.ContextTextEntry(
label = pullWithText,
onClick = onPullWith,
),
DropDownContentData(
ContextMenuElement.ContextTextEntry(
label = "Fetch all",
onClick = onFetchAll,
),

View File

@ -1,18 +1,15 @@
package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ExperimentalFoundationApi
@OptIn(ExperimentalFoundationApi::class)
fun pushContextMenuItems(
onPushWithTags: () -> Unit,
onForcePush: () -> Unit,
): List<DropDownContentData> {
): List<ContextMenuElement> {
return mutableListOf(
DropDownContentData(
ContextMenuElement.ContextTextEntry(
label = "Push including tags",
onClick = onPushWithTags,
),
DropDownContentData(
ContextMenuElement.ContextTextEntry(
label = "Force push",
onClick = onForcePush,
),

View File

@ -1,15 +1,16 @@
package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.ui.res.painterResource
import com.jetpackduba.gitnuro.AppIcons
@OptIn(ExperimentalFoundationApi::class)
fun stashContextMenuItems(
onStashWithMessage: () -> Unit,
): List<DropDownContentData> {
): List<ContextMenuElement> {
return mutableListOf(
DropDownContentData(
ContextMenuElement.ContextTextEntry(
label = "Stash with message",
onClick = onStashWithMessage,
icon = { painterResource(AppIcons.MESSAGE) }
),
)
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M4 4h16v12H5.17L4 17.17V4m0-2c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2H4zm2 10h12v2H6v-2zm0-3h12v2H6V9zm0-3h12v2H6V6z"/></svg>

After

Width:  |  Height:  |  Size: 300 B