Added instant tooltip for log avatar and menu entries

This commit is contained in:
Abdelilah El Aissaoui 2024-01-06 20:03:54 +01:00
parent 12b370ea79
commit 5a4f67bad6
No known key found for this signature in database
GPG key ID: 7587FC860F594869
4 changed files with 231 additions and 54 deletions

View file

@ -28,6 +28,7 @@ import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.extensions.ignoreKeyEvents
import com.jetpackduba.gitnuro.git.remote_operations.PullType
import com.jetpackduba.gitnuro.ui.components.InstantTooltip
import com.jetpackduba.gitnuro.ui.components.gitnuroViewModel
import com.jetpackduba.gitnuro.ui.context_menu.*
import com.jetpackduba.gitnuro.viewmodels.MenuViewModel
@ -50,19 +51,31 @@ fun Menu(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
MenuButton(
modifier = Modifier
.padding(start = 16.dp),
title = "Open",
icon = painterResource(AppIcons.OPEN),
onClick = onOpenAnotherRepository,
)
InstantTooltip(
text = "Open a different repository"
) {
MenuButton(
modifier = Modifier
.padding(start = 16.dp),
title = "Open",
icon = painterResource(AppIcons.OPEN),
onClick = onOpenAnotherRepository,
)
}
Spacer(modifier = Modifier.weight(1f))
val pullTooltip = if (isPullWithRebaseDefault) {
"Pull current branch with rebase"
} else {
"Pull current branch"
}
ExtendedMenuButton(
modifier = Modifier.padding(end = 4.dp),
title = "Pull",
tooltipText = pullTooltip,
icon = painterResource(AppIcons.DOWNLOAD),
onClick = { menuViewModel.pull(PullType.DEFAULT) },
extendedListItems = pullContextMenuItems(
@ -85,6 +98,7 @@ fun Menu(
ExtendedMenuButton(
title = "Push",
tooltipText = "Push current branch changes",
icon = painterResource(AppIcons.UPLOAD),
onClick = { menuViewModel.push() },
extendedListItems = pushContextMenuItems(
@ -99,25 +113,24 @@ fun Menu(
Spacer(modifier = Modifier.width(32.dp))
MenuButton(
title = "Branch",
icon = painterResource(AppIcons.BRANCH),
InstantTooltip(
text = "Create a new branch",
) {
onCreateBranch()
MenuButton(
title = "Branch",
icon = painterResource(AppIcons.BRANCH),
) {
onCreateBranch()
}
}
// MenuButton(
// title = "Merge",
// icon = painterResource("merge.svg"),
// ) {
// onCreateBranch()
// }
Spacer(modifier = Modifier.width(32.dp))
ExtendedMenuButton(
modifier = Modifier.padding(end = 4.dp),
title = "Stash",
tooltipText = "Stash uncommitted changes",
icon = painterResource(AppIcons.STASH),
onClick = { menuViewModel.stash() },
extendedListItems = stashContextMenuItems(
@ -125,19 +138,27 @@ fun Menu(
)
)
MenuButton(
title = "Pop",
icon = painterResource(AppIcons.APPLY_STASH),
) { menuViewModel.popStash() }
InstantTooltip(
text = "Pop the last stash"
) {
MenuButton(
title = "Pop",
icon = painterResource(AppIcons.APPLY_STASH),
) { menuViewModel.popStash() }
}
Spacer(modifier = Modifier.weight(1f))
MenuButton(
modifier = Modifier.padding(end = 4.dp),
title = "Terminal",
icon = painterResource(AppIcons.TERMINAL),
onClick = { menuViewModel.openTerminal() },
)
InstantTooltip(
text = "Open a terminal in the repository's path"
) {
MenuButton(
modifier = Modifier.padding(end = 4.dp),
title = "Terminal",
icon = painterResource(AppIcons.TERMINAL),
onClick = { menuViewModel.openTerminal() },
)
}
MenuButton(
modifier = Modifier.padding(end = 4.dp),
@ -146,12 +167,16 @@ fun Menu(
onClick = onQuickActions,
)
MenuButton(
modifier = Modifier.padding(end = 16.dp),
title = "Settings",
icon = painterResource(AppIcons.SETTINGS),
onClick = onShowSettingsDialog,
)
InstantTooltip(
text = "Gitnuro's settings",
modifier = Modifier.padding(end = 16.dp)
) {
MenuButton(
title = "Settings",
icon = painterResource(AppIcons.SETTINGS),
onClick = onShowSettingsDialog,
)
}
}
}
@ -195,6 +220,7 @@ fun ExtendedMenuButton(
modifier: Modifier = Modifier,
enabled: Boolean = true,
title: String,
tooltipText: String,
icon: Painter,
onClick: () -> Unit,
extendedListItems: List<ContextMenuElement>,
@ -207,26 +233,32 @@ fun ExtendedMenuButton(
.background(MaterialTheme.colors.surface)
.handMouseClickable { if (enabled) onClick() }
) {
Column(
InstantTooltip(
text = tooltipText,
modifier = Modifier
.fillMaxHeight()
.weight(1f),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
painter = icon,
contentDescription = title,
Column(
modifier = Modifier
.size(24.dp),
tint = MaterialTheme.colors.onBackground,
)
Text(
text = title,
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onBackground,
maxLines = 1,
)
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
painter = icon,
contentDescription = title,
modifier = Modifier
.size(24.dp),
tint = MaterialTheme.colors.onBackground,
)
Text(
text = title,
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onBackground,
maxLines = 1,
)
}
}
DropdownMenu(

View file

@ -0,0 +1,143 @@
package com.jetpackduba.gitnuro.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
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.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.unit.*
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.window.PopupProperties
import com.jetpackduba.gitnuro.theme.isDark
@Composable
fun InstantTooltip(
text: String,
modifier: Modifier = Modifier,
position: InstantTooltipPosition = InstantTooltipPosition.BOTTOM,
content: @Composable () -> Unit,
) {
val hoverInteractionSource = remember { MutableInteractionSource() }
val isHovered by hoverInteractionSource.collectIsHoveredAsState()
val (coordinates, setCoordinates) = remember { mutableStateOf<LayoutCoordinates?>(null) }
Box(
modifier = modifier
.hoverable(hoverInteractionSource)
.onGloballyPositioned {
setCoordinates(it)
},
) {
content()
}
if (isHovered && coordinates != null) {
Popup(
properties = PopupProperties(
focusable = false,
),
popupPositionProvider = object : PopupPositionProvider {
override fun calculatePosition(
anchorBounds: IntRect,
windowSize: IntSize,
layoutDirection: LayoutDirection,
popupContentSize: IntSize
): IntOffset {
val positionInWindow = coordinates.positionInWindow()
val contentSize = coordinates.size
val x = getXBasedOnTooltipPosition(position, positionInWindow, contentSize, popupContentSize) //
val y = getYBasedOnTooltipPosition(position, positionInWindow, contentSize, popupContentSize)
return IntOffset(x, y)
}
},
onDismissRequest = {}
) {
val padding = when(position) {
InstantTooltipPosition.TOP -> PaddingValues(bottom = 4.dp)
InstantTooltipPosition.BOTTOM -> PaddingValues(top = 4.dp)
InstantTooltipPosition.LEFT -> PaddingValues(end = 4.dp)
InstantTooltipPosition.RIGHT -> PaddingValues(start = 4.dp)
}
Box(
modifier = Modifier
.padding(padding)
.shadow(8.dp)
.clip(MaterialTheme.shapes.small)
.background(MaterialTheme.colors.background)
.width(IntrinsicSize.Max)
.run {
if (MaterialTheme.colors.isDark) {
this.border(
2.dp,
MaterialTheme.colors.onBackground.copy(alpha = 0.2f),
shape = MaterialTheme.shapes.small
)
} else
this
},
) {
Text(
text = text,
fontSize = 12.sp,
maxLines = 1,
color = MaterialTheme.colors.onBackground,
modifier = Modifier.padding(8.dp)
)
}
}
}
}
fun getXBasedOnTooltipPosition(
position: InstantTooltipPosition,
positionInWindow: Offset,
contentSize: IntSize,
popupContentSize: IntSize
): Int {
return when (position) {
InstantTooltipPosition.TOP, InstantTooltipPosition.BOTTOM -> (positionInWindow.x + (contentSize.width / 2)) - (popupContentSize.width / 2)
InstantTooltipPosition.LEFT -> positionInWindow.x - popupContentSize.width
InstantTooltipPosition.RIGHT -> positionInWindow.x + contentSize.width
}.toInt()
}
fun getYBasedOnTooltipPosition(
position: InstantTooltipPosition,
positionInWindow: Offset,
contentSize: IntSize,
popupContentSize: IntSize
): Int {
return when (position) {
InstantTooltipPosition.TOP -> positionInWindow.y - popupContentSize.height
InstantTooltipPosition.BOTTOM -> positionInWindow.y + contentSize.height
InstantTooltipPosition.LEFT, InstantTooltipPosition.RIGHT -> (positionInWindow.y + (contentSize.height / 2)) - (popupContentSize.height / 2)
}.toInt()
}
enum class InstantTooltipPosition {
TOP,
BOTTOM,
LEFT,
RIGHT
}

View file

@ -49,7 +49,6 @@ import kotlin.math.abs
private var lastCheck: Long = 0
private const val MIN_TIME_BETWEEN_POPUPS_IN_MS = 20
private const val BORDER_RADIUS = 4
@Composable
fun ContextMenu(items: () -> List<ContextMenuElement>, function: @Composable () -> Unit) {
@ -180,7 +179,7 @@ fun showPopup(x: Int, y: Int, contextMenuElements: List<ContextMenuElement>, onD
Box(
modifier = Modifier
.shadow(8.dp)
.clip(RoundedCornerShape(BORDER_RADIUS.dp))
.clip(MaterialTheme.shapes.small)
.background(MaterialTheme.colors.background)
.width(IntrinsicSize.Max)
.widthIn(min = 180.dp)
@ -189,7 +188,7 @@ fun showPopup(x: Int, y: Int, contextMenuElements: List<ContextMenuElement>, onD
this.border(
2.dp,
MaterialTheme.colors.onBackground.copy(alpha = 0.2f),
shape = RoundedCornerShape(BORDER_RADIUS.dp)
shape = MaterialTheme.shapes.small
)
} else
this
@ -380,14 +379,14 @@ class AppContextMenuRepresentation : ContextMenuRepresentation {
Column(
modifier = Modifier
.shadow(8.dp)
.clip(RoundedCornerShape(BORDER_RADIUS.dp))
.clip(MaterialTheme.shapes.small)
.background(MaterialTheme.colors.background)
.width(IntrinsicSize.Max)
.widthIn(min = 180.dp)
.verticalScroll(rememberScrollState())
.run {
if (border != null)
border(border, RoundedCornerShape(BORDER_RADIUS.dp))
border(border, MaterialTheme.shapes.small)
else
this
}

View file

@ -1058,7 +1058,10 @@ fun CommitNode(
)
}
} else {
Tooltip("${author.name} <${author.emailAddress}>") {
InstantTooltip(
"${author.name} <${author.emailAddress}>",
position = InstantTooltipPosition.RIGHT,
) {
Box(
modifier = modifier
.size(30.dp)