Reworked log to use a single list rather than 2 individual lists.

Also implemented showing author info on hovering  with a tooltip

Fixes #91
This commit is contained in:
Abdelilah El Aissaoui 2023-06-03 01:37:23 +02:00
parent e79a261b06
commit 819c2f1c95
No known key found for this signature in database
GPG Key ID: 7587FC860F594869

View File

@ -7,7 +7,6 @@ import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
@ -20,6 +19,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
@ -46,10 +46,7 @@ import com.jetpackduba.gitnuro.keybindings.KeybindingOption
import com.jetpackduba.gitnuro.keybindings.matchesBinding import com.jetpackduba.gitnuro.keybindings.matchesBinding
import com.jetpackduba.gitnuro.theme.* import com.jetpackduba.gitnuro.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.*
import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn
import com.jetpackduba.gitnuro.ui.components.gitnuroDynamicViewModel
import com.jetpackduba.gitnuro.ui.components.gitnuroViewModel
import com.jetpackduba.gitnuro.ui.context_menu.* import com.jetpackduba.gitnuro.ui.context_menu.*
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
@ -148,6 +145,19 @@ fun Log(
if (graphWidth.value < CANVAS_MIN_WIDTH) graphWidth = CANVAS_MIN_WIDTH.dp if (graphWidth.value < CANVAS_MIN_WIDTH) graphWidth = CANVAS_MIN_WIDTH.dp
val maxLinePosition = if (commitList.isNotEmpty())
commitList.maxLine
else
MIN_GRAPH_LANES
var graphRealWidth = ((maxLinePosition + MARGIN_GRAPH_LANES) * LANE_WIDTH).dp
// Using remember(graphRealWidth, graphWidth) makes the selected background color glitch when changing tabs
if (graphRealWidth < graphWidth) {
graphRealWidth = graphWidth
}
if (searchFilterValue is LogSearch.SearchResults) { if (searchFilterValue is LogSearch.SearchResults) {
SearchFilter(logViewModel, searchFilterValue) SearchFilter(logViewModel, searchFilterValue)
} }
@ -161,22 +171,28 @@ fun Log(
) )
Box { Box {
GraphList( Box(
commitList = commitList, Modifier
selectedCommit = selectedCommit, .width(graphWidth)
selectedItem = selectedItem, .fillMaxHeight()
repositoryState = repositoryState, ) {
horizontalScrollState = horizontalScrollState, Box(
graphWidth = graphWidth, modifier = Modifier
verticalScrollState = verticalScrollState, .fillMaxSize()
hasUncommitedChanges = hasUncommitedChanges, .horizontalScroll(horizontalScrollState)
commitsLimit = logStatus.commitsLimit, .padding(bottom = 8.dp)
) {
Box(
modifier = Modifier.width(graphRealWidth)
) )
}
}
// The commits' messages list overlaps with the graph list to catch all the click events but leaves // The commits' messages list overlaps with the graph list to catch all the click events but leaves
// a padding, so it doesn't cover the graph // a padding, so it doesn't cover the graph
MessagesList( MessagesList(
scrollState = verticalScrollState, scrollState = verticalScrollState,
horizontalScrollState = horizontalScrollState,
hasUncommitedChanges = hasUncommitedChanges, hasUncommitedChanges = hasUncommitedChanges,
searchFilter = if (searchFilterValue is LogSearch.SearchResults) searchFilterValue.commits else null, searchFilter = if (searchFilterValue is LogSearch.SearchResults) searchFilterValue.commits else null,
selectedCommit = selectedCommit, selectedCommit = selectedCommit,
@ -209,6 +225,7 @@ fun Log(
graphWidth = graphWidth, graphWidth = graphWidth,
) )
// Scrollbar used to scroll horizontally the graph nodes // Scrollbar used to scroll horizontally the graph nodes
// Added after every component to have the highest priority when clicking // Added after every component to have the highest priority when clicking
HorizontalScrollbar( HorizontalScrollbar(
@ -398,6 +415,7 @@ fun MessagesList(
onRebase: (Ref) -> Unit, onRebase: (Ref) -> Unit,
onShowLogDialog: (LogDialog) -> Unit, onShowLogDialog: (LogDialog) -> Unit,
graphWidth: Dp, graphWidth: Dp,
horizontalScrollState: ScrollState,
) { ) {
ScrollableLazyColumn( ScrollableLazyColumn(
state = scrollState, state = scrollState,
@ -410,17 +428,28 @@ fun MessagesList(
repositoryState.isCherryPicking repositoryState.isCherryPicking
) { ) {
item { item {
Box(
modifier = Modifier.height(LINE_HEIGHT.dp)
.clipToBounds()
.fillMaxWidth()
.fastClickable { logViewModel.selectUncommitedChanges() }
) {
UncommitedChangesGraphNode(
hasPreviousCommits = commitList.isNotEmpty(),
isSelected = selectedItem is SelectedItem.UncommitedChanges,
modifier = Modifier.offset(-horizontalScrollState.value.dp)
)
UncommitedChangesLine( UncommitedChangesLine(
graphWidth = graphWidth, graphWidth = graphWidth,
isSelected = selectedItem == SelectedItem.UncommitedChanges, isSelected = selectedItem == SelectedItem.UncommitedChanges,
statusSummary = logStatus.statusSummary, statusSummary = logStatus.statusSummary,
repositoryState = repositoryState, repositoryState = repositoryState,
onUncommitedChangesSelected = {
logViewModel.selectUncommitedChanges()
}
) )
} }
} }
}
// Setting a key makes the graph preserve the scroll position when a new line has been added on top (uncommited changes) // Setting a key makes the graph preserve the scroll position when a new line has been added on top (uncommited changes)
// Therefore, after popping a stash, the uncommited changes wouldn't be visible and requires the user scrolling. // Therefore, after popping a stash, the uncommited changes wouldn't be visible and requires the user scrolling.
items(items = commitList) { graphNode -> items(items = commitList) { graphNode ->
@ -431,6 +460,7 @@ fun MessagesList(
isSelected = selectedCommit?.name == graphNode.name, isSelected = selectedCommit?.name == graphNode.name,
currentBranch = logStatus.currentBranch, currentBranch = logStatus.currentBranch,
matchesSearchFilter = searchFilter?.contains(graphNode), matchesSearchFilter = searchFilter?.contains(graphNode),
horizontalScrollState = horizontalScrollState,
showCreateNewBranch = { onShowLogDialog(LogDialog.NewBranch(graphNode)) }, showCreateNewBranch = { onShowLogDialog(LogDialog.NewBranch(graphNode)) },
showCreateNewTag = { onShowLogDialog(LogDialog.NewTag(graphNode)) }, showCreateNewTag = { onShowLogDialog(LogDialog.NewTag(graphNode)) },
resetBranch = { onShowLogDialog(LogDialog.ResetBranch(graphNode)) }, resetBranch = { onShowLogDialog(LogDialog.ResetBranch(graphNode)) },
@ -467,101 +497,6 @@ fun MessagesList(
} }
} }
@Composable
fun GraphList(
commitList: GraphCommitList,
horizontalScrollState: ScrollState,
verticalScrollState: LazyListState,
graphWidth: Dp,
hasUncommitedChanges: Boolean,
selectedCommit: RevCommit?,
selectedItem: SelectedItem,
commitsLimit: Int,
repositoryState: RepositoryState,
) {
val maxLinePosition = if (commitList.isNotEmpty())
commitList.maxLine
else
MIN_GRAPH_LANES
var graphRealWidth = ((maxLinePosition + MARGIN_GRAPH_LANES) * LANE_WIDTH).dp
// Using remember(graphRealWidth, graphWidth) makes the selected background color glitch when changing tabs
if (graphRealWidth < graphWidth) {
graphRealWidth = graphWidth
}
Box(
Modifier
.width(graphWidth)
.fillMaxHeight()
) {
Box(
modifier = Modifier
.fillMaxSize()
.horizontalScroll(horizontalScrollState)
.padding(bottom = 8.dp)
) {
LazyColumn(
state = verticalScrollState, modifier = Modifier.width(graphRealWidth)
) {
if (
hasUncommitedChanges ||
repositoryState.isMerging ||
repositoryState.isRebasing ||
repositoryState.isCherryPicking
) {
item {
Row(
modifier = Modifier
.height(LINE_HEIGHT.dp)
.fillMaxWidth(),
) {
UncommitedChangesGraphNode(
modifier = Modifier.fillMaxSize(),
hasPreviousCommits = commitList.isNotEmpty(),
isSelected = selectedItem is SelectedItem.UncommitedChanges,
)
}
}
}
items(items = commitList) { graphNode ->
val nodeColor = colors[graphNode.lane.position % colors.size]
Row(
modifier = Modifier
.height(LINE_HEIGHT.dp)
.fillMaxWidth(),
) {
CommitsGraphLine(
modifier = Modifier.fillMaxSize(),
plotCommit = graphNode,
nodeColor = nodeColor,
isSelected = selectedCommit?.name == graphNode.name,
)
}
}
// Spacing when the commits limit is present
if (commitsLimit >= 0 && commitsLimit <= commitList.count()) {
item {
Box(
modifier = Modifier
.height(LINE_HEIGHT.dp),
)
}
}
item {
Box(modifier = Modifier.height(LOG_BOTTOM_PADDING.dp))
}
}
}
}
}
@Composable @Composable
fun LogDialogs( fun LogDialogs(
logViewModel: LogViewModel, logViewModel: LogViewModel,
@ -670,13 +605,10 @@ fun UncommitedChangesLine(
isSelected: Boolean, isSelected: Boolean,
repositoryState: RepositoryState, repositoryState: RepositoryState,
statusSummary: StatusSummary, statusSummary: StatusSummary,
onUncommitedChangesSelected: () -> Unit,
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.height(40.dp) .height(40.dp)
.fillMaxWidth()
.handMouseClickable { onUncommitedChangesSelected() }
.padding(start = graphWidth) .padding(start = graphWidth)
.backgroundIf(isSelected, MaterialTheme.colors.backgroundSelected) .backgroundIf(isSelected, MaterialTheme.colors.backgroundSelected)
.padding(DIVIDER_WIDTH.dp), .padding(DIVIDER_WIDTH.dp),
@ -767,7 +699,6 @@ fun SummaryEntry(
} }
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun CommitLine( fun CommitLine(
graphWidth: Dp, graphWidth: Dp,
@ -784,6 +715,7 @@ fun CommitLine(
onRevCommitSelected: () -> Unit, onRevCommitSelected: () -> Unit,
onRebaseInteractive: () -> Unit, onRebaseInteractive: () -> Unit,
onChangeDefaultUpstreamBranch: (Ref) -> Unit, onChangeDefaultUpstreamBranch: (Ref) -> Unit,
horizontalScrollState: ScrollState,
) { ) {
val isLastCommitOfCurrentBranch = currentBranch?.objectId?.name == graphNode.id.name val isLastCommitOfCurrentBranch = currentBranch?.objectId?.name == graphNode.id.name
@ -804,10 +736,33 @@ fun CommitLine(
Box( Box(
modifier = Modifier modifier = Modifier
.fastClickable(graphNode, logViewModel) { onRevCommitSelected() } .fastClickable(graphNode, logViewModel) { onRevCommitSelected() }
) {
val nodeColor = colors[graphNode.lane.position % colors.size]
Box {
Row(
modifier = Modifier
.clipToBounds()
.height(LINE_HEIGHT.dp)
.fillMaxWidth()
.offset(-horizontalScrollState.value.dp)
) {
CommitsGraphLine(
modifier = Modifier
.fillMaxHeight(),
plotCommit = graphNode,
nodeColor = nodeColor,
isSelected = isSelected,
)
}
}
Box(
modifier = Modifier
.padding(start = graphWidth) .padding(start = graphWidth)
.height(LINE_HEIGHT.dp) .height(LINE_HEIGHT.dp)
.background(MaterialTheme.colors.background)
.backgroundIf(isSelected, MaterialTheme.colors.backgroundSelected) .backgroundIf(isSelected, MaterialTheme.colors.backgroundSelected)
) { ) {
if (matchesSearchFilter == true) { if (matchesSearchFilter == true) {
@ -825,7 +780,6 @@ fun CommitLine(
.fillMaxWidth() .fillMaxWidth()
.padding(end = 4.dp), .padding(end = 4.dp),
) { ) {
val nodeColor = colors[graphNode.lane.position % colors.size]
CommitMessage( CommitMessage(
commit = graphNode, commit = graphNode,
refs = graphNode.refs, refs = graphNode.refs,
@ -845,6 +799,7 @@ fun CommitLine(
} }
} }
} }
}
} }
@Composable @Composable
@ -973,7 +928,10 @@ fun CommitsGraphLine(
Box( Box(
modifier = modifier modifier = modifier
.backgroundIf(isSelected, MaterialTheme.colors.backgroundSelected) .backgroundIf(isSelected, MaterialTheme.colors.backgroundSelected)
.fillMaxHeight(),
contentAlignment = Alignment.CenterStart,
) { ) {
val itemPosition = plotCommit.lane.position val itemPosition = plotCommit.lane.position
Canvas( Canvas(
@ -1043,6 +1001,8 @@ fun CommitNode(
plotCommit: GraphNode, plotCommit: GraphNode,
color: Color, color: Color,
) { ) {
val author = plotCommit.authorIdent
Tooltip("${author.name} <${author.emailAddress}>") {
Box( Box(
modifier = modifier modifier = modifier
.size(30.dp) .size(30.dp)
@ -1055,6 +1015,7 @@ fun CommitNode(
color = color, color = color,
) )
} }
}
} }
@Composable @Composable