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:
parent
e79a261b06
commit
819c2f1c95
@ -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,
|
||||||
@ -846,6 +800,7 @@ fun CommitLine(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CommitMessage(
|
fun CommitMessage(
|
||||||
@ -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)
|
||||||
@ -1056,6 +1016,7 @@ fun CommitNode(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UncommitedChangesGraphNode(
|
fun UncommitedChangesGraphNode(
|
||||||
|
Loading…
Reference in New Issue
Block a user