Skip to content

Commit

Permalink
Added instant tooltip for log avatar and menu entries
Browse files Browse the repository at this point in the history
  • Loading branch information
JetpackDuba committed Jan 6, 2024
1 parent 12b370e commit 5a4f67b
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 54 deletions.
128 changes: 80 additions & 48 deletions src/main/kotlin/com/jetpackduba/gitnuro/ui/Menu.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -85,6 +98,7 @@ fun Menu(

ExtendedMenuButton(
title = "Push",
tooltipText = "Push current branch changes",
icon = painterResource(AppIcons.UPLOAD),
onClick = { menuViewModel.push() },
extendedListItems = pushContextMenuItems(
Expand All @@ -99,45 +113,52 @@ 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(
onStashWithMessage = onStashWithMessage
)
)

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),
Expand All @@ -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,
)
}
}
}

Expand Down Expand Up @@ -195,6 +220,7 @@ fun ExtendedMenuButton(
modifier: Modifier = Modifier,
enabled: Boolean = true,
title: String,
tooltipText: String,
icon: Painter,
onClick: () -> Unit,
extendedListItems: List<ContextMenuElement>,
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
Loading

0 comments on commit 5a4f67b

Please sign in to comment.