Created custom implementation of context menu and added new icons

This commit is contained in:
Abdelilah El Aissaoui 2022-09-28 15:07:42 +02:00
parent 8df62ef2b7
commit e7c36c6e90
35 changed files with 354 additions and 146 deletions

View File

@ -27,6 +27,7 @@ dependencies {
implementation(compose.desktop.currentOs) implementation(compose.desktop.currentOs)
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
implementation(compose.desktop.components.splitPane) implementation(compose.desktop.components.splitPane)
implementation(compose("org.jetbrains.compose.ui:ui-util"))
implementation("org.eclipse.jgit:org.eclipse.jgit:6.2.0.202206071550-r") implementation("org.eclipse.jgit:org.eclipse.jgit:6.2.0.202206071550-r")
implementation("org.apache.sshd:sshd-core:2.9.0") implementation("org.apache.sshd:sshd-core:2.9.0")
implementation("com.google.dagger:dagger:2.43.2") implementation("com.google.dagger:dagger:2.43.2")
@ -56,7 +57,7 @@ tasks.withType<KotlinCompile> {
compose.desktop { compose.desktop {
application { application {
mainClass = "MainKt" mainClass = "com.jetpackduba.gitnuro.MainKt"
nativeDistributions { nativeDistributions {
includeAllModules = true includeAllModules = true

View File

@ -63,7 +63,7 @@ fun rememberNetworkImageOrNull(url: String, placeHolderImageRes: String? = null)
val cacheImageUsed = remember { ValueHolder(false) } val cacheImageUsed = remember { ValueHolder(false) }
var image by remember(url) { var image by remember(url) {
val cachedImage = NetworkImageLoader.loadCachedImage(url) val cachedImage = networkImageLoader.loadCachedImage(url)
val image: ImageBitmap? = when { val image: ImageBitmap? = when {
cachedImage != null -> { cachedImage != null -> {

View File

@ -1,4 +1,4 @@
import com.jetpackduba.gitnuro.App package com.jetpackduba.gitnuro
fun main() { fun main() {
val main = App() val main = App()

View File

@ -27,6 +27,7 @@ val lightTheme = ColorsScheme(
hoverScrollbar = Color(0xFF0070D8), hoverScrollbar = Color(0xFF0070D8),
diffLineAdded = Color(0xFFd7ebd0), diffLineAdded = Color(0xFFd7ebd0),
diffLineRemoved = Color(0xFFf0d4d4), diffLineRemoved = Color(0xFFf0d4d4),
isLight = true,
) )
@ -55,7 +56,7 @@ val darkBlueTheme = ColorsScheme(
hoverScrollbar = Color(0xFFCCCCCC), hoverScrollbar = Color(0xFFCCCCCC),
diffLineAdded = Color(0xFF566f5a), diffLineAdded = Color(0xFF566f5a),
diffLineRemoved = Color(0xFF6f585e), diffLineRemoved = Color(0xFF6f585e),
isLight = false,
) )
val darkGrayTheme = ColorsScheme( val darkGrayTheme = ColorsScheme(
@ -83,4 +84,5 @@ val darkGrayTheme = ColorsScheme(
hoverScrollbar = Color(0xFFCCCCCC), hoverScrollbar = Color(0xFFCCCCCC),
diffLineAdded = Color(0xFF5b7059), diffLineAdded = Color(0xFF5b7059),
diffLineRemoved = Color(0xFF74595c), diffLineRemoved = Color(0xFF74595c),
isLight = false,
) )

View File

@ -14,7 +14,6 @@ import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
// TODO Add line added + removed colors, graph colors and icons color for added/modified/removed files.
@Serializable @Serializable
data class ColorsScheme( data class ColorsScheme(
val primary: Color, val primary: Color,
@ -42,6 +41,7 @@ data class ColorsScheme(
val hoverScrollbar: Color, val hoverScrollbar: Color,
val diffLineAdded: Color, val diffLineAdded: Color,
val diffLineRemoved: Color, val diffLineRemoved: Color,
val isLight: Boolean,
) { ) {
fun toComposeColors(): Colors { fun toComposeColors(): Colors {
return Colors( return Colors(
@ -57,7 +57,7 @@ data class ColorsScheme(
onBackground = this.primaryText, onBackground = this.primaryText,
onSurface = this.primaryText, onSurface = this.primaryText,
onError = this.onError, onError = this.onError,
isLight = true, // property specific for some colors, we don't care about this as all our components are customized isLight = isLight,
) )
} }
} }

View File

@ -55,6 +55,7 @@ fun typography() = Typography(
body2 = TextStyle( body2 = TextStyle(
fontSize = 13.sp, fontSize = 13.sp,
color = MaterialTheme.colors.primaryTextColor, color = MaterialTheme.colors.primaryTextColor,
fontWeight = FontWeight.Normal,
letterSpacing = LETTER_SPACING.sp, letterSpacing = LETTER_SPACING.sp,
), ),
caption = TextStyle( caption = TextStyle(

View File

@ -1,6 +1,5 @@
package com.jetpackduba.gitnuro.ui package com.jetpackduba.gitnuro.ui
import androidx.compose.foundation.ContextMenuArea
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
@ -16,11 +15,11 @@ import com.jetpackduba.gitnuro.extensions.simpleName
import com.jetpackduba.gitnuro.theme.secondaryTextColor import com.jetpackduba.gitnuro.theme.secondaryTextColor
import com.jetpackduba.gitnuro.ui.components.SideMenuPanel import com.jetpackduba.gitnuro.ui.components.SideMenuPanel
import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu
import com.jetpackduba.gitnuro.ui.context_menu.branchContextMenuItems import com.jetpackduba.gitnuro.ui.context_menu.branchContextMenuItems
import com.jetpackduba.gitnuro.viewmodels.BranchesViewModel import com.jetpackduba.gitnuro.viewmodels.BranchesViewModel
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun Branches( fun Branches(
branchesViewModel: BranchesViewModel, branchesViewModel: BranchesViewModel,
@ -69,7 +68,7 @@ private fun BranchLineEntry(
onPushToRemoteBranch: () -> Unit, onPushToRemoteBranch: () -> Unit,
onPullFromRemoteBranch: () -> Unit, onPullFromRemoteBranch: () -> Unit,
) { ) {
ContextMenuArea( ContextMenu(
items = { items = {
branchContextMenuItems( branchContextMenuItems(
branch = branch, branch = branch,

View File

@ -21,6 +21,7 @@ import com.jetpackduba.gitnuro.viewmodels.CommitChangesStatus
import com.jetpackduba.gitnuro.viewmodels.CommitChangesViewModel import com.jetpackduba.gitnuro.viewmodels.CommitChangesViewModel
import com.jetpackduba.gitnuro.extensions.* import com.jetpackduba.gitnuro.extensions.*
import com.jetpackduba.gitnuro.theme.* import com.jetpackduba.gitnuro.theme.*
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.diff.DiffEntry
@ -228,7 +229,7 @@ fun CommitLogChanges(
.fillMaxSize() .fillMaxSize()
) { ) {
items(items = diffEntries) { diffEntry -> items(items = diffEntries) { diffEntry ->
ContextMenuArea( ContextMenu(
items = { items = {
commitedChangesEntriesContextMenuItems( commitedChangesEntriesContextMenuItems(
diffEntry, diffEntry,

View File

@ -1,9 +1,7 @@
@file:OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) @file:OptIn(ExperimentalComposeUiApi::class)
package com.jetpackduba.gitnuro.ui package com.jetpackduba.gitnuro.ui
import androidx.compose.foundation.ContextMenuArea
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -23,6 +21,7 @@ import com.jetpackduba.gitnuro.theme.primaryTextColor
import com.jetpackduba.gitnuro.ui.components.SideMenuPanel import com.jetpackduba.gitnuro.ui.components.SideMenuPanel
import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry
import com.jetpackduba.gitnuro.ui.components.VerticalExpandable import com.jetpackduba.gitnuro.ui.components.VerticalExpandable
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu
import com.jetpackduba.gitnuro.ui.context_menu.remoteBranchesContextMenu import com.jetpackduba.gitnuro.ui.context_menu.remoteBranchesContextMenu
import com.jetpackduba.gitnuro.ui.context_menu.remoteContextMenu import com.jetpackduba.gitnuro.ui.context_menu.remoteContextMenu
import com.jetpackduba.gitnuro.ui.dialogs.EditRemotesDialog import com.jetpackduba.gitnuro.ui.dialogs.EditRemotesDialog
@ -30,6 +29,7 @@ import com.jetpackduba.gitnuro.viewmodels.RemoteView
import com.jetpackduba.gitnuro.viewmodels.RemotesViewModel import com.jetpackduba.gitnuro.viewmodels.RemotesViewModel
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref
@OptIn(ExperimentalComposeUiApi::class)
@Composable @Composable
fun Remotes( fun Remotes(
remotesViewModel: RemotesViewModel, remotesViewModel: RemotesViewModel,
@ -85,7 +85,6 @@ fun Remotes(
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
private fun RemoteRow( private fun RemoteRow(
remote: RemoteView, remote: RemoteView,
@ -106,7 +105,7 @@ private fun RemoteRow(
val branches = remote.remoteInfo.branchesList val branches = remote.remoteInfo.branchesList
Column { Column {
branches.forEach { branch -> branches.forEach { branch ->
ContextMenuArea( ContextMenu(
items = { items = {
remoteBranchesContextMenu( remoteBranchesContextMenu(
onDeleteBranch = { onDeleteBranch(branch) } onDeleteBranch = { onDeleteBranch(branch) }

View File

@ -1,9 +1,5 @@
@file:OptIn(ExperimentalFoundationApi::class)
package com.jetpackduba.gitnuro.ui package com.jetpackduba.gitnuro.ui
import androidx.compose.foundation.ContextMenuArea
import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -11,6 +7,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import com.jetpackduba.gitnuro.ui.components.SideMenuPanel import com.jetpackduba.gitnuro.ui.components.SideMenuPanel
import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenuElement
import com.jetpackduba.gitnuro.ui.context_menu.stashesContextMenuItems import com.jetpackduba.gitnuro.ui.context_menu.stashesContextMenuItems
import com.jetpackduba.gitnuro.viewmodels.StashStatus import com.jetpackduba.gitnuro.viewmodels.StashStatus
import com.jetpackduba.gitnuro.viewmodels.StashesViewModel import com.jetpackduba.gitnuro.viewmodels.StashesViewModel
@ -58,9 +56,9 @@ fun Stashes(
private fun StashRow( private fun StashRow(
stash: RevCommit, stash: RevCommit,
onClick: () -> Unit, onClick: () -> Unit,
contextItems: List<ContextMenuItem>, contextItems: List<ContextMenuElement>,
) { ) {
ContextMenuArea( ContextMenu(
items = { contextItems } items = { contextItems }
) { ) {
SideMenuSubentry( SideMenuSubentry(

View File

@ -1,7 +1,5 @@
package com.jetpackduba.gitnuro.ui package com.jetpackduba.gitnuro.ui
import androidx.compose.foundation.ContextMenuArea
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
@ -15,11 +13,11 @@ import com.jetpackduba.gitnuro.theme.secondaryTextColor
import com.jetpackduba.gitnuro.ui.components.SideMenuPanel import com.jetpackduba.gitnuro.ui.components.SideMenuPanel
import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry
import com.jetpackduba.gitnuro.ui.components.Tooltip import com.jetpackduba.gitnuro.ui.components.Tooltip
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu
import com.jetpackduba.gitnuro.ui.context_menu.submoduleContextMenuItems import com.jetpackduba.gitnuro.ui.context_menu.submoduleContextMenuItems
import com.jetpackduba.gitnuro.viewmodels.SubmodulesViewModel import com.jetpackduba.gitnuro.viewmodels.SubmodulesViewModel
import org.eclipse.jgit.submodule.SubmoduleStatus import org.eclipse.jgit.submodule.SubmoduleStatus
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun Submodules( fun Submodules(
submodulesViewModel: SubmodulesViewModel, submodulesViewModel: SubmodulesViewModel,
@ -42,13 +40,12 @@ fun Submodules(
) )
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
private fun SubmoduleLineEntry( private fun SubmoduleLineEntry(
submodulePair: Pair<String, SubmoduleStatus>, submodulePair: Pair<String, SubmoduleStatus>,
onInitializeModule: () -> Unit, onInitializeModule: () -> Unit,
) { ) {
ContextMenuArea( ContextMenu(
items = { items = {
submoduleContextMenuItems( submoduleContextMenuItems(
submodulePair.second, submodulePair.second,

View File

@ -1,6 +1,5 @@
package com.jetpackduba.gitnuro.ui package com.jetpackduba.gitnuro.ui
import androidx.compose.foundation.ContextMenuArea
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -9,6 +8,7 @@ import androidx.compose.ui.res.painterResource
import com.jetpackduba.gitnuro.extensions.simpleName import com.jetpackduba.gitnuro.extensions.simpleName
import com.jetpackduba.gitnuro.ui.components.SideMenuPanel import com.jetpackduba.gitnuro.ui.components.SideMenuPanel
import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry import com.jetpackduba.gitnuro.ui.components.SideMenuSubentry
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu
import com.jetpackduba.gitnuro.ui.context_menu.tagContextMenuItems import com.jetpackduba.gitnuro.ui.context_menu.tagContextMenuItems
import com.jetpackduba.gitnuro.viewmodels.TagsViewModel import com.jetpackduba.gitnuro.viewmodels.TagsViewModel
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref
@ -47,7 +47,7 @@ private fun TagRow(
onCheckoutTag: () -> Unit, onCheckoutTag: () -> Unit,
onDeleteTag: () -> Unit, onDeleteTag: () -> Unit,
) { ) {
ContextMenuArea( ContextMenu(
items = { items = {
tagContextMenuItems( tagContextMenuItems(
onCheckoutTag = onCheckoutTag, onCheckoutTag = onCheckoutTag,

View File

@ -35,14 +35,11 @@ import com.jetpackduba.gitnuro.keybindings.KeybindingOption
import com.jetpackduba.gitnuro.keybindings.matchesBinding import com.jetpackduba.gitnuro.keybindings.matchesBinding
import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn
import com.jetpackduba.gitnuro.ui.components.SecondaryButton import com.jetpackduba.gitnuro.ui.components.SecondaryButton
import com.jetpackduba.gitnuro.ui.context_menu.DropDownContent
import com.jetpackduba.gitnuro.ui.context_menu.DropDownContentData
import com.jetpackduba.gitnuro.ui.context_menu.EntryType
import com.jetpackduba.gitnuro.ui.context_menu.statusEntriesContextMenuItems
import com.jetpackduba.gitnuro.viewmodels.StageStatus import com.jetpackduba.gitnuro.viewmodels.StageStatus
import com.jetpackduba.gitnuro.viewmodels.StatusViewModel import com.jetpackduba.gitnuro.viewmodels.StatusViewModel
import com.jetpackduba.gitnuro.extensions.* import com.jetpackduba.gitnuro.extensions.*
import com.jetpackduba.gitnuro.theme.* import com.jetpackduba.gitnuro.theme.*
import com.jetpackduba.gitnuro.ui.context_menu.*
import org.eclipse.jgit.lib.RepositoryState import org.eclipse.jgit.lib.RepositoryState
@Composable @Composable
@ -496,7 +493,7 @@ private fun EntriesList(
lazyListState: LazyListState, lazyListState: LazyListState,
onDiffEntrySelected: (StatusEntry) -> Unit, onDiffEntrySelected: (StatusEntry) -> Unit,
onDiffEntryOptionSelected: (StatusEntry) -> Unit, onDiffEntryOptionSelected: (StatusEntry) -> Unit,
onGenerateContextMenu: (StatusEntry) -> List<ContextMenuItem>, onGenerateContextMenu: (StatusEntry) -> List<ContextMenuElement>,
onAllAction: () -> Unit, onAllAction: () -> Unit,
allActionTitle: String, allActionTitle: String,
selectedEntryType: DiffEntryType?, selectedEntryType: DiffEntryType?,
@ -571,7 +568,7 @@ private fun FileEntry(
actionColor: Color, actionColor: Color,
onClick: () -> Unit, onClick: () -> Unit,
onButtonClick: () -> Unit, onButtonClick: () -> Unit,
onGenerateContextMenu: (StatusEntry) -> List<ContextMenuItem>, onGenerateContextMenu: (StatusEntry) -> List<ContextMenuElement>,
) { ) {
val hoverInteraction = remember { MutableInteractionSource() } val hoverInteraction = remember { MutableInteractionSource() }
val isHovered by hoverInteraction.collectIsHoveredAsState() val isHovered by hoverInteraction.collectIsHoveredAsState()
@ -582,7 +579,7 @@ private fun FileEntry(
.fillMaxWidth() .fillMaxWidth()
.hoverable(hoverInteraction) .hoverable(hoverInteraction)
) { ) {
ContextMenuArea( ContextMenu(
items = { items = {
onGenerateContextMenu(statusEntry) onGenerateContextMenu(statusEntry)
}, },

View File

@ -1,7 +1,5 @@
package com.jetpackduba.gitnuro.ui.components package com.jetpackduba.gitnuro.ui.components
import androidx.compose.foundation.ContextMenuArea
import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -10,6 +8,8 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenu
import com.jetpackduba.gitnuro.ui.context_menu.ContextMenuElement
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
@ -21,13 +21,13 @@ fun <T> SideMenuPanel(
onExpand: () -> Unit, onExpand: () -> Unit,
itemContent: @Composable (T) -> Unit, itemContent: @Composable (T) -> Unit,
headerHoverIcon: @Composable (() -> Unit)? = null, headerHoverIcon: @Composable (() -> Unit)? = null,
contextItems: () -> List<ContextMenuItem> = { emptyList() }, contextItems: () -> List<ContextMenuElement> = { emptyList() },
) { ) {
VerticalExpandable( VerticalExpandable(
isExpanded = isExpanded, isExpanded = isExpanded,
onExpand = onExpand, onExpand = onExpand,
header = { header = {
ContextMenuArea( ContextMenu(
items = contextItems items = contextItems
) { ) {
SideMenuEntry( SideMenuEntry(

View File

@ -1,12 +1,10 @@
package com.jetpackduba.gitnuro.ui.context_menu package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem import androidx.compose.ui.res.painterResource
import androidx.compose.foundation.ExperimentalFoundationApi
import com.jetpackduba.gitnuro.extensions.isHead import com.jetpackduba.gitnuro.extensions.isHead
import com.jetpackduba.gitnuro.extensions.simpleLogName import com.jetpackduba.gitnuro.extensions.simpleLogName
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref
@OptIn(ExperimentalFoundationApi::class)
fun branchContextMenuItems( fun branchContextMenuItems(
branch: Ref, branch: Ref,
isCurrentBranch: Boolean, isCurrentBranch: Boolean,
@ -19,47 +17,51 @@ fun branchContextMenuItems(
onDeleteRemoteBranch: () -> Unit = {}, onDeleteRemoteBranch: () -> Unit = {},
onPushToRemoteBranch: () -> Unit, onPushToRemoteBranch: () -> Unit,
onPullFromRemoteBranch: () -> Unit, onPullFromRemoteBranch: () -> Unit,
): List<ContextMenuItem> { ): List<ContextMenuElement> {
return mutableListOf<ContextMenuItem>().apply { return mutableListOf<ContextMenuElement>().apply {
if (!isCurrentBranch) { if (!isCurrentBranch) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Checkout branch", label = "Checkout branch",
icon = { painterResource("start.svg") },
onClick = onCheckoutBranch onClick = onCheckoutBranch
) )
) )
if (currentBranch != null && !currentBranch.isHead) { if (currentBranch != null && !currentBranch.isHead) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Merge branch", label = "Merge branch",
onClick = onMergeBranch onClick = onMergeBranch
) )
) )
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Rebase branch", label = "Rebase branch",
onClick = onRebaseBranch onClick = onRebaseBranch
) )
) )
add(ContextMenuElement.ContextSeparator)
} }
} }
if (isLocal && !isCurrentBranch) { if (isLocal && !isCurrentBranch) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Delete branch", label = "Delete branch",
icon = { painterResource("delete.svg") },
onClick = onDeleteBranch onClick = onDeleteBranch
) )
) )
} }
if (!isLocal && currentBranch != null && !currentBranch.isHead) { if (!isLocal && currentBranch != null && !currentBranch.isHead) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Push ${currentBranch.simpleLogName} to ${branch.simpleLogName}", label = "Push ${currentBranch.simpleLogName} to ${branch.simpleLogName}",
onClick = onPushToRemoteBranch onClick = onPushToRemoteBranch
) )
) )
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Pull ${branch.simpleLogName} to ${currentBranch.simpleLogName}", label = "Pull ${branch.simpleLogName} to ${currentBranch.simpleLogName}",
onClick = onPullFromRemoteBranch onClick = onPullFromRemoteBranch
) )
@ -67,11 +69,16 @@ fun branchContextMenuItems(
} }
if (!isLocal) { if (!isLocal) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Delete remote branch", label = "Delete remote branch",
icon = { painterResource("delete.svg") },
onClick = onDeleteRemoteBranch onClick = onDeleteRemoteBranch
), ),
) )
} }
if(lastOrNull() == ContextMenuElement.ContextSeparator) {
removeLast()
}
} }
} }

View File

@ -2,6 +2,7 @@ package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.ui.res.painterResource
import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.diff.DiffEntry
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@ -9,19 +10,20 @@ fun commitedChangesEntriesContextMenuItems(
diffEntry: DiffEntry, diffEntry: DiffEntry,
onBlame: () -> Unit, onBlame: () -> Unit,
onHistory: () -> Unit, onHistory: () -> Unit,
): List<ContextMenuItem> { ): List<ContextMenuElement> {
return mutableListOf<ContextMenuItem>().apply { return mutableListOf<ContextMenuElement>().apply {
if (diffEntry.changeType != DiffEntry.ChangeType.ADD || if (diffEntry.changeType != DiffEntry.ChangeType.ADD ||
diffEntry.changeType != DiffEntry.ChangeType.DELETE diffEntry.changeType != DiffEntry.ChangeType.DELETE
) { ) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Blame file", label = "Blame file",
icon = { painterResource("blame.svg") },
onClick = onBlame, onClick = onBlame,
) )
) )
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "File history", label = "File history",
onClick = onHistory, onClick = onHistory,
) )

View File

@ -0,0 +1,226 @@
package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
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.graphics.painter.Painter
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.unit.*
import androidx.compose.ui.util.fastAll
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
import com.jetpackduba.gitnuro.keybindings.matchesBinding
import com.jetpackduba.gitnuro.theme.primaryTextColor
import com.jetpackduba.gitnuro.theme.secondaryTextColor
import java.awt.event.MouseEvent
import kotlin.math.abs
private var lastCheck: Long = 0
private const val MIN_TIME_BETWEEN_POPUPS = 20
@Composable
fun ContextMenu(items: () -> List<ContextMenuElement>, function: @Composable () -> Unit) {
Box(modifier = Modifier.contextMenu(items), propagateMinConstraints = true) {
function()
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun Modifier.contextMenu(items: () -> List<ContextMenuElement>): Modifier {
var lastMouseEventState by remember { mutableStateOf<MouseEvent?>(null) }
val mod = this.pointerInput(Unit) {
while (true) {
val lastMouseEvent = awaitPointerEventScope { awaitEventFirstDown() }
val mouseEvent = lastMouseEvent.awtEventOrNull
if (mouseEvent != null) {
if (!lastMouseEvent.toString().contains("MOUSE_MOVED"))
println(lastMouseEvent.toString())
if (lastMouseEvent.button.isSecondary) {
val currentCheck = System.currentTimeMillis()
if (lastCheck != 0L && currentCheck - lastCheck < MIN_TIME_BETWEEN_POPUPS) {
println("IGNORE POPUP TRIGGERED!")
} else {
println("POPUP TRIGGERED!")
println("X: ${mouseEvent.x}\nY: ${mouseEvent.y}")
lastCheck = currentCheck
lastMouseEventState = mouseEvent
}
}
}
}
}
if (lastMouseEventState != null) {
showPopup(
lastMouseEventState!!.x,
lastMouseEventState!!.y,
items(),
onDismissRequest = { lastMouseEventState = null })
}
return mod
}
@Composable
fun showPopup(x: Int, y: Int, contextMenuElements: List<ContextMenuElement>, onDismissRequest: () -> Unit) {
LaunchedEffect(contextMenuElements) {
println("Items count ${contextMenuElements.count()}")
}
Popup(
focusable = true,
popupPositionProvider = object : PopupPositionProvider {
override fun calculatePosition(
anchorBounds: IntRect,
windowSize: IntSize,
layoutDirection: LayoutDirection,
popupContentSize: IntSize
): IntOffset {
val resultY = if (popupContentSize.height > windowSize.height) {
0 // If the popup is taller than the window itself
} else if (y + popupContentSize.height > windowSize.height) {
// If the end of the popup would go out of bounds.
// Move the Y a bit to the top to make it fit
y - abs(windowSize.height - (y + popupContentSize.height))
} else {
y
}
val resultX = if (x + popupContentSize.width > windowSize.width && popupContentSize.width < x) {
// If the end of the popup would go out of bounds.
// Move the X a bit to the left to make it fit
x - abs(windowSize.width - (x + popupContentSize.width))
} else {
x
}
return IntOffset(resultX, resultY)
}
},
onDismissRequest = onDismissRequest
) {
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
Box(
modifier = Modifier
.shadow(4.dp)
.width(300.dp)
.background(MaterialTheme.colors.background)
.run {
return@run if (!MaterialTheme.colors.isLight) {
this.border(1.dp, MaterialTheme.colors.primaryTextColor.copy(alpha = 0.2f))
} else
this
}
.focusRequester(focusRequester)
.focusable()
.onPreviewKeyEvent { keyEvent ->
if (keyEvent.matchesBinding(KeybindingOption.EXIT)) {
onDismissRequest()
true
} else
false
},
) {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
for (item in contextMenuElements) {
when (item) {
is ContextMenuElement.ContextTextEntry -> TextEntry(item, onDismissRequest = onDismissRequest)
ContextMenuElement.ContextSeparator -> Separator()
}
}
}
}
}
}
@Composable
fun Separator() {
Box(
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth()
.height(1.dp)
.background(MaterialTheme.colors.primaryTextColor.copy(alpha = 0.4f))
)
}
@Composable
internal fun focusRequesterAndModifier(): Pair<FocusRequester, Modifier> {
val focusRequester = remember { FocusRequester() }
return focusRequester to Modifier.focusRequester(focusRequester)
}
@Composable
fun TextEntry(contextTextEntry: ContextMenuElement.ContextTextEntry, onDismissRequest: () -> Unit) {
val icon = contextTextEntry.icon
Row(
modifier = Modifier
.clickable {
onDismissRequest()
contextTextEntry.onClick()
}
.padding(horizontal = 16.dp, vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(modifier = Modifier.size(24.dp).padding(end = 8.dp)) {
if (icon != null) {
Icon(
painter = icon(),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
tint = (MaterialTheme.colors.secondaryTextColor.copy(alpha = 0.8f))
)
}
}
Text(
contextTextEntry.label,
style = MaterialTheme.typography.body2,
modifier = Modifier.fillMaxWidth(),
)
}
}
sealed interface ContextMenuElement {
data class ContextTextEntry(
val label: String,
val icon: @Composable (() -> Painter)? = null,
val onClick: () -> Unit = {}
) : ContextMenuElement
object ContextSeparator : ContextMenuElement
}
private suspend fun AwaitPointerEventScope.awaitEventFirstDown(): PointerEvent {
var event: PointerEvent
do {
event = awaitPointerEvent()
} while (
!event.changes.fastAll { it.changedToDown() }
)
return event
}

View File

@ -1,7 +1,6 @@
package com.jetpackduba.gitnuro.ui.context_menu package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem import androidx.compose.ui.res.painterResource
import androidx.compose.foundation.ExperimentalFoundationApi
fun logContextMenu( fun logContextMenu(
onCheckoutCommit: () -> Unit, onCheckoutCommit: () -> Unit,
@ -12,32 +11,38 @@ fun logContextMenu(
onResetBranch: () -> Unit, onResetBranch: () -> Unit,
onRebaseInteractive: () -> Unit, onRebaseInteractive: () -> Unit,
) = listOf( ) = listOf(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Checkout commit", label = "Checkout commit",
icon = { painterResource("start.svg") },
onClick = onCheckoutCommit onClick = onCheckoutCommit
), ),
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Create branch", label = "Create branch",
icon = { painterResource("branch.svg") },
onClick = onCreateNewBranch onClick = onCreateNewBranch
), ),
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Create tag", label = "Create tag",
icon = { painterResource("tag.svg") },
onClick = onCreateNewTag onClick = onCreateNewTag
), ),
ContextMenuItem( ContextMenuElement.ContextSeparator,
ContextMenuElement.ContextTextEntry(
label = "Rebase interactive", label = "Rebase interactive",
onClick = onRebaseInteractive onClick = onRebaseInteractive
), ),
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Revert commit", label = "Revert commit",
icon = { painterResource("revert.svg") },
onClick = onRevertCommit onClick = onRevertCommit
), ),
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Cherry-pick commit", label = "Cherry-pick commit",
onClick = onCherryPickCommit onClick = onCherryPickCommit
), ),
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Reset current branch to this commit", label = "Reset current branch to this commit",
icon = { painterResource("undo.svg") },
onClick = onResetBranch onClick = onResetBranch
), ),
) )

View File

@ -4,13 +4,15 @@ package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.ui.res.painterResource
fun remoteBranchesContextMenu( fun remoteBranchesContextMenu(
onDeleteBranch: () -> Unit onDeleteBranch: () -> Unit
): List<ContextMenuItem> { ): List<ContextMenuElement> {
return mutableListOf( return listOf(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Delete remote branch", label = "Delete remote branch",
icon = { painterResource("delete.svg") },
onClick = onDeleteBranch onClick = onDeleteBranch
), ),
) )

View File

@ -1,13 +1,9 @@
package com.jetpackduba.gitnuro.ui.context_menu package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi
@OptIn(ExperimentalFoundationApi::class)
fun remoteContextMenu( fun remoteContextMenu(
onEditRemotes: () -> Unit, onEditRemotes: () -> Unit,
) = listOf( ): List<ContextMenuElement> = listOf(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Edit remotes", label = "Edit remotes",
onClick = onEditRemotes onClick = onEditRemotes
), ),

View File

@ -1,23 +0,0 @@
package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi
import com.jetpackduba.gitnuro.git.workspace.StatusEntry
import com.jetpackduba.gitnuro.git.workspace.StatusType
@OptIn(ExperimentalFoundationApi::class)
fun stagedEntriesContextMenuItems(
diffEntry: StatusEntry,
onReset: () -> Unit,
): List<ContextMenuItem> {
return mutableListOf<ContextMenuItem>().apply {
if (diffEntry.statusType != StatusType.ADDED) {
add(
ContextMenuItem(
label = "Reset",
onClick = onReset,
)
)
}
}
}

View File

@ -1,25 +1,26 @@
package com.jetpackduba.gitnuro.ui.context_menu package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem import androidx.compose.ui.res.painterResource
import androidx.compose.foundation.ExperimentalFoundationApi
@OptIn(ExperimentalFoundationApi::class)
fun stashesContextMenuItems( fun stashesContextMenuItems(
onApply: () -> Unit, onApply: () -> Unit,
onPop: () -> Unit, onPop: () -> Unit,
onDelete: () -> Unit, onDelete: () -> Unit,
): List<ContextMenuItem> { ): List<ContextMenuElement> {
return mutableListOf( return listOf(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Apply stash", label = "Apply stash",
icon = { painterResource("apply_stash.svg") },
onClick = onApply onClick = onApply
), ),
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Pop stash", label = "Pop stash",
icon = { painterResource("apply_stash.svg") },
onClick = onPop onClick = onPop
), ),
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Drop stash", label = "Drop stash",
icon = { painterResource("delete.svg") },
onClick = onDelete onClick = onDelete
), ),
) )

View File

@ -1,11 +1,9 @@
package com.jetpackduba.gitnuro.ui.context_menu package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem import androidx.compose.ui.res.painterResource
import androidx.compose.foundation.ExperimentalFoundationApi
import com.jetpackduba.gitnuro.git.workspace.StatusEntry import com.jetpackduba.gitnuro.git.workspace.StatusEntry
import com.jetpackduba.gitnuro.git.workspace.StatusType import com.jetpackduba.gitnuro.git.workspace.StatusType
@OptIn(ExperimentalFoundationApi::class)
fun statusEntriesContextMenuItems( fun statusEntriesContextMenuItems(
statusEntry: StatusEntry, statusEntry: StatusEntry,
entryType: EntryType, entryType: EntryType,
@ -13,26 +11,28 @@ fun statusEntriesContextMenuItems(
onDelete: () -> Unit = {}, onDelete: () -> Unit = {},
onBlame: () -> Unit, onBlame: () -> Unit,
onHistory: () -> Unit, onHistory: () -> Unit,
): List<ContextMenuItem> { ): List<ContextMenuElement> {
return mutableListOf<ContextMenuItem>().apply { return mutableListOf<ContextMenuElement>().apply {
if (statusEntry.statusType != StatusType.ADDED) { if (statusEntry.statusType != StatusType.ADDED) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Reset", label = "Reset",
icon = { painterResource("undo.svg") },
onClick = onReset, onClick = onReset,
) )
) )
if (statusEntry.statusType != StatusType.REMOVED) { if (statusEntry.statusType != StatusType.REMOVED) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Blame file", label = "Blame file",
icon = { painterResource("blame.svg") },
onClick = onBlame, onClick = onBlame,
) )
) )
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "File history", label = "File history",
onClick = onHistory, onClick = onHistory,
) )
@ -45,8 +45,9 @@ fun statusEntriesContextMenuItems(
statusEntry.statusType != StatusType.REMOVED statusEntry.statusType != StatusType.REMOVED
) { ) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Delete file", label = "Delete file",
icon = { painterResource("delete.svg") },
onClick = onDelete, onClick = onDelete,
) )
) )

View File

@ -1,19 +1,16 @@
package com.jetpackduba.gitnuro.ui.context_menu package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi
import org.eclipse.jgit.submodule.SubmoduleStatus import org.eclipse.jgit.submodule.SubmoduleStatus
import org.eclipse.jgit.submodule.SubmoduleStatusType import org.eclipse.jgit.submodule.SubmoduleStatusType
@OptIn(ExperimentalFoundationApi::class)
fun submoduleContextMenuItems( fun submoduleContextMenuItems(
submoduleStatus: SubmoduleStatus, submoduleStatus: SubmoduleStatus,
onInitializeModule: () -> Unit, onInitializeModule: () -> Unit,
): List<ContextMenuItem> { ): List<ContextMenuElement> {
return mutableListOf<ContextMenuItem>().apply { return mutableListOf<ContextMenuElement>().apply {
if (submoduleStatus.type == SubmoduleStatusType.UNINITIALIZED) { if (submoduleStatus.type == SubmoduleStatusType.UNINITIALIZED) {
add( add(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Initialize submodule", label = "Initialize submodule",
onClick = onInitializeModule onClick = onInitializeModule
) )

View File

@ -1,20 +1,21 @@
package com.jetpackduba.gitnuro.ui.context_menu package com.jetpackduba.gitnuro.ui.context_menu
import androidx.compose.foundation.ContextMenuItem
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.ui.res.painterResource
@OptIn(ExperimentalFoundationApi::class)
fun tagContextMenuItems( fun tagContextMenuItems(
onCheckoutTag: () -> Unit, onCheckoutTag: () -> Unit,
onDeleteTag: () -> Unit, onDeleteTag: () -> Unit,
): List<ContextMenuItem> { ): List<ContextMenuElement> {
return mutableListOf( return mutableListOf(
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Checkout tag", label = "Checkout tag",
icon = { painterResource("start.svg") },
onClick = onCheckoutTag onClick = onCheckoutTag
), ),
ContextMenuItem( ContextMenuElement.ContextTextEntry(
label = "Delete tag", label = "Delete tag",
icon = { painterResource("delete.svg") },
onClick = onDeleteTag onClick = onDeleteTag
) )
) )

View File

@ -38,19 +38,14 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.jetpackduba.gitnuro.app.extensions.*
import com.jetpackduba.gitnuro.git.workspace.StatusSummary import com.jetpackduba.gitnuro.git.workspace.StatusSummary
import com.jetpackduba.gitnuro.git.graph.GraphCommitList import com.jetpackduba.gitnuro.git.graph.GraphCommitList
import com.jetpackduba.gitnuro.git.graph.GraphNode import com.jetpackduba.gitnuro.git.graph.GraphNode
import com.jetpackduba.gitnuro.keybindings.KeybindingOption import com.jetpackduba.gitnuro.keybindings.KeybindingOption
import com.jetpackduba.gitnuro.keybindings.matchesBinding import com.jetpackduba.gitnuro.keybindings.matchesBinding
import com.jetpackduba.gitnuro.app.theme.*
import com.jetpackduba.gitnuro.ui.SelectedItem import com.jetpackduba.gitnuro.ui.SelectedItem
import com.jetpackduba.gitnuro.ui.components.AvatarImage import com.jetpackduba.gitnuro.ui.components.AvatarImage
import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn
import com.jetpackduba.gitnuro.ui.context_menu.branchContextMenuItems
import com.jetpackduba.gitnuro.ui.context_menu.logContextMenu
import com.jetpackduba.gitnuro.ui.context_menu.tagContextMenuItems
import com.jetpackduba.gitnuro.ui.dialogs.NewBranchDialog import com.jetpackduba.gitnuro.ui.dialogs.NewBranchDialog
import com.jetpackduba.gitnuro.ui.dialogs.NewTagDialog import com.jetpackduba.gitnuro.ui.dialogs.NewTagDialog
import com.jetpackduba.gitnuro.ui.dialogs.ResetBranchDialog import com.jetpackduba.gitnuro.ui.dialogs.ResetBranchDialog
@ -59,6 +54,7 @@ import com.jetpackduba.gitnuro.viewmodels.LogStatus
import com.jetpackduba.gitnuro.viewmodels.LogViewModel import com.jetpackduba.gitnuro.viewmodels.LogViewModel
import com.jetpackduba.gitnuro.extensions.* import com.jetpackduba.gitnuro.extensions.*
import com.jetpackduba.gitnuro.theme.* import com.jetpackduba.gitnuro.theme.*
import com.jetpackduba.gitnuro.ui.context_menu.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref
import org.eclipse.jgit.lib.RepositoryState import org.eclipse.jgit.lib.RepositoryState
@ -750,7 +746,7 @@ fun CommitLine(
onRevCommitSelected: () -> Unit, onRevCommitSelected: () -> Unit,
onRebaseInteractive: () -> Unit, onRebaseInteractive: () -> Unit,
) { ) {
ContextMenuArea( ContextMenu(
items = { items = {
logContextMenu( logContextMenu(
onCheckoutCommit = { logViewModel.checkoutCommit(graphNode) }, onCheckoutCommit = { logViewModel.checkoutCommit(graphNode) },
@ -1122,7 +1118,7 @@ fun TagChip(
) )
} }
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
@Composable @Composable
fun RefChip( fun RefChip(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -1130,7 +1126,7 @@ fun RefChip(
icon: String, icon: String,
color: Color, color: Color,
onCheckoutRef: () -> Unit, onCheckoutRef: () -> Unit,
contextMenuItemsList: () -> List<ContextMenuItem>, contextMenuItemsList: () -> List<ContextMenuElement>,
endingContent: @Composable () -> Unit = {}, endingContent: @Composable () -> Unit = {},
) { ) {
Box( Box(
@ -1141,7 +1137,7 @@ fun RefChip(
.combinedClickable(onDoubleClick = onCheckoutRef, onClick = {}) .combinedClickable(onDoubleClick = onCheckoutRef, onClick = {})
.pointerHoverIcon(PointerIconDefaults.Hand) .pointerHoverIcon(PointerIconDefaults.Hand)
) { ) {
ContextMenuArea( ContextMenu(
items = contextMenuItemsList items = contextMenuItemsList
) { ) {
Row( Row(

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><path d="M0,0h24v24H0V0z" fill="none"/></g><g><path d="M7,9H2V7h5V9z M7,12H2v2h5V12z M20.59,19l-3.83-3.83C15.96,15.69,15.02,16,14,16c-2.76,0-5-2.24-5-5s2.24-5,5-5s5,2.24,5,5 c0,1.02-0.31,1.96-0.83,2.75L22,17.59L20.59,19z M17,11c0-1.65-1.35-3-3-3s-3,1.35-3,3s1.35,3,3,3S17,12.65,17,11z M2,19h10v-2H2 V19z"/></g></svg>

After

Width:  |  Height:  |  Size: 455 B

View File

@ -1,4 +1,7 @@
<?xml version="1.0" ?> <svg width="54" height="54" viewBox="0 0 54 54" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg height="1024" width="640" xmlns="http://www.w3.org/2000/svg"> <line x1="14" y1="15" x2="14" y2="40" stroke="black" stroke-width="4"/>
<path d="M512 192c-70.625 0-128 57.344-128 128 0 47.219 25.875 88.062 64 110.281V448c0 0 0 128-128 128-53.062 0-94.656 11.375-128 28.812V302.28099999999995c38.156-22.219 64-63.062 64-110.281 0-70.656-57.344-128-128-128S0 121.34400000000005 0 192c0 47.219 25.844 88.062 64 110.281V721.75C25.844 743.938 0 784.75 0 832c0 70.625 57.344 128 128 128s128-57.375 128-128c0-33.5-13.188-63.75-34.25-86.625C240.375 722.5 270.656 704 320 704c254 0 256-256 256-256v-17.719c38.125-22.219 64-63.062 64-110.281C640 249.34400000000005 582.625 192 512 192zM128 128c35.406 0 64 28.594 64 64s-28.594 64-64 64-64-28.594-64-64S92.594 128 128 128zM128 896c-35.406 0-64-28.625-64-64 0-35.312 28.594-64 64-64s64 28.688 64 64C192 867.375 163.406 896 128 896zM512 384c-35.375 0-64-28.594-64-64s28.625-64 64-64 64 28.594 64 64S547.375 384 512 384z"/> <circle cx="14" cy="8" r="6" stroke="black" stroke-width="4"/>
<path d="M46 19C46 22.3137 43.3137 25 40 25C36.6863 25 34 22.3137 34 19C34 15.6863 36.6863 13 40 13C43.3137 13 46 15.6863 46 19Z" stroke="black" stroke-width="4"/>
<circle cx="14" cy="46" r="6" stroke="black" stroke-width="4"/>
<path d="M13.8484 38.636L15.9135 36.8638C18.4322 34.7022 21.6179 33.4731 24.9357 33.3828L31.5028 33.204C35.676 33.0904 39 29.6747 39 25.5V25.5" stroke="black" stroke-width="4"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 924 B

After

Width:  |  Height:  |  Size: 644 B

View File

@ -1,4 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <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="M12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11c1.56.1 2.78 1.41 2.78 2.96 0 1.65-1.35 3-3 3H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95C8.08 7.14 9.94 6 12 6m0-2C9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96C18.67 6.59 15.64 4 12 4z"/></svg>
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"/>
</svg>

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 480 B

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="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z"/></svg>

After

Width:  |  Height:  |  Size: 252 B

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="M14 12c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm-2-9c-4.97 0-9 4.03-9 9H0l4 4 4-4H5c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.51 0-2.91-.49-4.06-1.3l-1.42 1.44C8.04 20.3 9.94 21 12 21c4.97 0 9-4.03 9-9s-4.03-9-9-9z"/></svg>

After

Width:  |  Height:  |  Size: 377 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M14.59,7.41L18.17,11H6v2h12.17l-3.59,3.59L16,18l6-6l-6-6L14.59,7.41z M2,6v12h2V6H2z"/></svg>

After

Width:  |  Height:  |  Size: 279 B

View File

@ -1,4 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <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="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58s1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41s-.23-1.06-.59-1.42zM13 20.01L4 11V4h7v-.01l9 9-7 7.02z"/><circle cx="6.5" cy="6.5" r="1.5"/></svg>
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>
</svg>

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 402 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/><path d="M20,6h-8l-2-2H4C2.9,4,2.01,4.9,2.01,6L2,18c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C22,6.9,21.1,6,20,6z M14,16H6v-2h8V16z M18,12H6v-2h12V12z"/></g></svg> <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/><path d="M20,6h-8l-2-2H4C2.9,4,2.01,4.9,2.01,6L2,18c0,1.1,0.9,2,2,2h16.77c0.68,0,1.23-0.56,1.23-1.23V8C22,6.9,21.1,6,20,6z M20,18L4,18V6h5.17l2,2H20V18z M18,12H6v-2h12V12z M14,16H6v-2h8V16z"/></g></svg>

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 383 B

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="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"/></svg>

After

Width:  |  Height:  |  Size: 301 B