Added new log graph
This commit is contained in:
parent
03afe8c1d2
commit
6d0f621bcd
5 changed files with 384 additions and 83 deletions
|
@ -26,6 +26,7 @@ dependencies {
|
||||||
implementation("org.eclipse.jgit:org.eclipse.jgit:5.13.0.202109080827-r")
|
implementation("org.eclipse.jgit:org.eclipse.jgit:5.13.0.202109080827-r")
|
||||||
implementation("org.apache.sshd:sshd-core:2.7.0")
|
implementation("org.apache.sshd:sshd-core:2.7.0")
|
||||||
implementation("com.google.dagger:dagger:2.39.1")
|
implementation("com.google.dagger:dagger:2.39.1")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-reflect:1.5.21")
|
||||||
kapt("com.google.dagger:dagger-compiler:2.39.1")
|
kapt("com.google.dagger:dagger-compiler:2.39.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/main/kotlin/app/CommitNode.kt
Normal file
13
src/main/kotlin/app/CommitNode.kt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
|
||||||
|
class CommitNode(val revCommit: RevCommit) {
|
||||||
|
private val children = mutableListOf<CommitNode>()
|
||||||
|
|
||||||
|
fun addChild(node: CommitNode) {
|
||||||
|
if(children.find { it.revCommit.id == node.revCommit.id } == null) {
|
||||||
|
children.add(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/main/kotlin/app/extensions/PlotCommit.kt
Normal file
30
src/main/kotlin/app/extensions/PlotCommit.kt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
@file:Suppress("UNCHECKED_CAST")
|
||||||
|
|
||||||
|
package app.extensions
|
||||||
|
|
||||||
|
import org.eclipse.jgit.revplot.PlotCommit
|
||||||
|
import org.eclipse.jgit.revplot.PlotLane
|
||||||
|
import kotlin.reflect.full.memberProperties
|
||||||
|
import kotlin.reflect.jvm.isAccessible
|
||||||
|
|
||||||
|
val PlotCommit<PlotLane>.reflectForkingOffLanes: Array<PlotLane>
|
||||||
|
get() {
|
||||||
|
val f = PlotCommit::class.memberProperties.find { it.name == "forkingOffLanes" }
|
||||||
|
f?.let {
|
||||||
|
it.isAccessible = true
|
||||||
|
return it.get(this) as Array<PlotLane>
|
||||||
|
}
|
||||||
|
|
||||||
|
return emptyArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
val PlotCommit<PlotLane>.reflectMergingLanes: Array<PlotLane>
|
||||||
|
get() {
|
||||||
|
val f = PlotCommit::class.memberProperties.find { it.name == "mergingLanes" }
|
||||||
|
f?.let {
|
||||||
|
it.isAccessible = true
|
||||||
|
return it.get(this) as Array<PlotLane>
|
||||||
|
}
|
||||||
|
|
||||||
|
return emptyArray()
|
||||||
|
}
|
|
@ -6,11 +6,16 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.revplot.PlotCommit
|
||||||
|
import org.eclipse.jgit.revplot.PlotCommitList
|
||||||
|
import org.eclipse.jgit.revplot.PlotLane
|
||||||
|
import org.eclipse.jgit.revplot.PlotWalk
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LogManager @Inject constructor() {
|
class LogManager @Inject constructor() {
|
||||||
private val _logStatus = MutableStateFlow<LogStatus>(LogStatus.Loaded(listOf()))
|
private val _logStatus = MutableStateFlow<LogStatus>(LogStatus.Loading)
|
||||||
|
|
||||||
val logStatus: StateFlow<LogStatus>
|
val logStatus: StateFlow<LogStatus>
|
||||||
get() = _logStatus
|
get() = _logStatus
|
||||||
|
@ -18,15 +23,21 @@ class LogManager @Inject constructor() {
|
||||||
suspend fun loadLog(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun loadLog(git: Git) = withContext(Dispatchers.IO) {
|
||||||
_logStatus.value = LogStatus.Loading
|
_logStatus.value = LogStatus.Loading
|
||||||
|
|
||||||
val log: Iterable<RevCommit> = git.log().call()
|
val commitList = PlotCommitList<PlotLane>()
|
||||||
|
val walk = PlotWalk(git.repository)
|
||||||
|
|
||||||
|
walk.markStart(walk.parseCommit(git.repository.resolve("HEAD")));
|
||||||
|
commitList.source(walk)
|
||||||
|
|
||||||
|
commitList.fillTo(Int.MAX_VALUE)
|
||||||
|
|
||||||
ensureActive()
|
ensureActive()
|
||||||
|
|
||||||
_logStatus.value = LogStatus.Loaded(log.toList())
|
_logStatus.value = LogStatus.Loaded(commitList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class LogStatus {
|
sealed class LogStatus {
|
||||||
object Loading : LogStatus()
|
object Loading : LogStatus()
|
||||||
data class Loaded(val commits: List<RevCommit>) : LogStatus()
|
data class Loaded(val plotCommitList: PlotCommitList<PlotLane>) : LogStatus()
|
||||||
}
|
}
|
|
@ -1,28 +1,57 @@
|
||||||
package app.ui
|
package app.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
|
import androidx.compose.foundation.gestures.draggable
|
||||||
|
import androidx.compose.foundation.gestures.rememberDraggableState
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.material.Card
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.Divider
|
|
||||||
import androidx.compose.material.MaterialTheme
|
|
||||||
import androidx.compose.material.Text
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||||
|
import androidx.compose.ui.input.pointer.PointerIcon
|
||||||
|
import androidx.compose.ui.input.pointer.pointerIcon
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
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.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import app.ui.components.ScrollableLazyColumn
|
import app.extensions.reflectForkingOffLanes
|
||||||
|
import app.extensions.reflectMergingLanes
|
||||||
import app.extensions.toSmartSystemString
|
import app.extensions.toSmartSystemString
|
||||||
import app.git.GitManager
|
import app.git.GitManager
|
||||||
import app.git.LogStatus
|
import app.git.LogStatus
|
||||||
import org.eclipse.jgit.revwalk.RevCommit
|
import app.theme.headerBackground
|
||||||
import app.theme.primaryTextColor
|
import app.theme.primaryTextColor
|
||||||
import app.theme.secondaryTextColor
|
import app.theme.secondaryTextColor
|
||||||
|
import app.ui.components.ScrollableLazyColumn
|
||||||
|
import org.eclipse.jgit.revplot.PlotCommit
|
||||||
|
import org.eclipse.jgit.revplot.PlotCommitList
|
||||||
|
import org.eclipse.jgit.revplot.PlotLane
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
|
||||||
|
private val colors = listOf(
|
||||||
|
Color(0xFF42a5f5),
|
||||||
|
Color(0xFFef5350),
|
||||||
|
Color(0xFFe78909c),
|
||||||
|
Color(0xFFff7043),
|
||||||
|
Color(0xFF66bb6a),
|
||||||
|
Color(0xFFec407a),
|
||||||
|
)
|
||||||
|
|
||||||
|
private const val CANVAS_MIN_WIDTH = 100
|
||||||
|
|
||||||
|
// TODO Min size for message column
|
||||||
|
// TODO Horizontal scroll for the graph
|
||||||
|
@OptIn(
|
||||||
|
ExperimentalDesktopApi::class, ExperimentalFoundationApi::class,
|
||||||
|
ExperimentalComposeUiApi::class
|
||||||
|
)
|
||||||
@Composable
|
@Composable
|
||||||
fun Log(
|
fun Log(
|
||||||
gitManager: GitManager,
|
gitManager: GitManager,
|
||||||
|
@ -35,74 +64,121 @@ fun Log(
|
||||||
|
|
||||||
val selectedUncommited = remember { mutableStateOf(false) }
|
val selectedUncommited = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val log = if (logStatus is LogStatus.Loaded) {
|
if (logStatus is LogStatus.Loaded) {
|
||||||
logStatus.commits
|
val commitList = logStatus.plotCommitList
|
||||||
} else
|
|
||||||
listOf()
|
|
||||||
|
|
||||||
|
Card(
|
||||||
Card(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
.background(MaterialTheme.colors.surface)
|
|
||||||
.fillMaxSize()
|
|
||||||
) {
|
|
||||||
val hasUncommitedChanges by gitManager.hasUncommitedChanges.collectAsState()
|
|
||||||
|
|
||||||
ScrollableLazyColumn(
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
.background(MaterialTheme.colors.surface)
|
.background(MaterialTheme.colors.surface)
|
||||||
.fillMaxSize(),
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
|
val hasUncommitedChanges by gitManager.hasUncommitedChanges.collectAsState()
|
||||||
|
var weightMod by remember { mutableStateOf(0f) }
|
||||||
|
var graphWidth = (CANVAS_MIN_WIDTH + weightMod).dp//(weightMod / 500)
|
||||||
|
|
||||||
if (hasUncommitedChanges)
|
if (graphWidth.value < CANVAS_MIN_WIDTH)
|
||||||
item {
|
graphWidth = CANVAS_MIN_WIDTH.dp
|
||||||
val textColor = if (selectedUncommited.value) {
|
|
||||||
MaterialTheme.colors.primary
|
|
||||||
} else
|
|
||||||
MaterialTheme.colors.primaryTextColor
|
|
||||||
|
|
||||||
Column(
|
ScrollableLazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(MaterialTheme.colors.surface)
|
||||||
|
.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
|
||||||
|
stickyHeader {
|
||||||
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(40.dp)
|
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.height(32.dp)
|
||||||
selectedIndex.value = -1
|
.background(MaterialTheme.colors.headerBackground),
|
||||||
selectedUncommited.value = true
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
onUncommitedChangesSelected()
|
|
||||||
},
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.weight(2f))
|
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "Uncommited changes",
|
modifier = Modifier
|
||||||
fontStyle = FontStyle.Italic,
|
.width(graphWidth)
|
||||||
modifier = Modifier.padding(start = 16.dp),
|
.padding(start = 8.dp),
|
||||||
|
text = "Graph",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colors.primary,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
color = textColor,
|
maxLines = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(2f))
|
DividerLog(
|
||||||
|
modifier = Modifier.draggable(rememberDraggableState {
|
||||||
|
weightMod += it
|
||||||
|
}, Orientation.Horizontal)
|
||||||
|
)
|
||||||
|
|
||||||
Divider()
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 8.dp)
|
||||||
|
.width(graphWidth),
|
||||||
|
text = "Message",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colors.primary,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsIndexed(items = log) { index, item ->
|
if (hasUncommitedChanges)
|
||||||
val textColor = if (selectedIndex.value == index) {
|
item {
|
||||||
MaterialTheme.colors.primary
|
val textColor = if (selectedUncommited.value) {
|
||||||
} else
|
MaterialTheme.colors.primary
|
||||||
MaterialTheme.colors.primaryTextColor
|
} else
|
||||||
|
MaterialTheme.colors.primaryTextColor
|
||||||
|
|
||||||
val secondaryTextColor = if (selectedIndex.value == index) {
|
Row(
|
||||||
MaterialTheme.colors.primary
|
modifier = Modifier
|
||||||
} else
|
.height(40.dp)
|
||||||
MaterialTheme.colors.secondaryTextColor
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
selectedIndex.value = -1
|
||||||
|
selectedUncommited.value = true
|
||||||
|
onUncommitedChangesSelected()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
val hasPreviousCommits = remember(commitList) { commitList.count() > 0 }
|
||||||
|
|
||||||
|
UncommitedChangesGraphLine(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(graphWidth),
|
||||||
|
hasPreviousCommits = hasPreviousCommits,
|
||||||
|
)
|
||||||
|
|
||||||
Column {
|
DividerLog(
|
||||||
Spacer(modifier = Modifier.weight(2f))
|
modifier = Modifier
|
||||||
|
.draggable(
|
||||||
|
rememberDraggableState {
|
||||||
|
weightMod += it
|
||||||
|
},
|
||||||
|
Orientation.Horizontal
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.weight(2f))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "Uncommited changes",
|
||||||
|
fontStyle = FontStyle.Italic,
|
||||||
|
modifier = Modifier.padding(start = 16.dp),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = textColor,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(2f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsIndexed(items = commitList) { index, item ->
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(40.dp)
|
.height(40.dp)
|
||||||
|
@ -112,36 +188,206 @@ fun Log(
|
||||||
selectedUncommited.value = false
|
selectedUncommited.value = false
|
||||||
onRevCommitSelected(item)
|
onRevCommitSelected(item)
|
||||||
},
|
},
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
) {
|
||||||
|
CommitsGraphLine(
|
||||||
|
plotCommit = item,
|
||||||
Text(
|
commitList = commitList,
|
||||||
text = item.shortMessage,
|
hasUncommitedChanges = hasUncommitedChanges,
|
||||||
modifier = Modifier.padding(start = 16.dp),
|
modifier = Modifier
|
||||||
fontSize = 14.sp,
|
.width(graphWidth)
|
||||||
color = textColor,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.weight(2f))
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = item.committerIdent.`when`.toSmartSystemString(),
|
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
|
||||||
fontSize = 12.sp,
|
|
||||||
color = secondaryTextColor,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DividerLog(
|
||||||
|
modifier = Modifier
|
||||||
|
.draggable(
|
||||||
|
rememberDraggableState {
|
||||||
|
weightMod += it
|
||||||
|
},
|
||||||
|
Orientation.Horizontal
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
CommitMessage(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
commit = item,
|
||||||
|
selected = selectedIndex.value == index
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(2f))
|
|
||||||
Divider()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CommitMessage(modifier: Modifier = Modifier, commit: RevCommit, selected: Boolean) {
|
||||||
|
val textColor = if (selected) {
|
||||||
|
MaterialTheme.colors.primary
|
||||||
|
} else
|
||||||
|
MaterialTheme.colors.primaryTextColor
|
||||||
|
|
||||||
|
val secondaryTextColor = if (selected) {
|
||||||
|
MaterialTheme.colors.primary
|
||||||
|
} else
|
||||||
|
MaterialTheme.colors.secondaryTextColor
|
||||||
|
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.weight(2f))
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = commit.shortMessage,
|
||||||
|
modifier = Modifier.padding(start = 16.dp),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = textColor,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.weight(2f))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = commit.committerIdent.`when`.toSmartSystemString(),
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = secondaryTextColor,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.weight(2f))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun DividerLog(modifier: Modifier) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(8.dp)
|
||||||
|
.then(modifier)
|
||||||
|
.pointerIcon(PointerIcon.Hand)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxHeight()
|
||||||
|
.width(1.dp)
|
||||||
|
.background(color = MaterialTheme.colors.primary)
|
||||||
|
.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CommitsGraphLine(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
commitList: PlotCommitList<PlotLane>,
|
||||||
|
plotCommit: PlotCommit<PlotLane>,
|
||||||
|
hasUncommitedChanges: Boolean,
|
||||||
|
) {
|
||||||
|
val passingLanes = remember(plotCommit) {
|
||||||
|
val passingLanesList = mutableListOf<PlotLane>()
|
||||||
|
commitList.findPassingThrough(plotCommit, passingLanesList)
|
||||||
|
|
||||||
|
passingLanesList
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val forkingOffLanes = remember(plotCommit) { plotCommit.reflectForkingOffLanes }
|
||||||
|
val mergingLanes = remember(plotCommit) { plotCommit.reflectMergingLanes }
|
||||||
|
|
||||||
|
Box(modifier = modifier) {
|
||||||
|
Canvas(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
|
val itemPosition = plotCommit.lane.position
|
||||||
|
clipRect {
|
||||||
|
if (plotCommit.childCount > 0 || hasUncommitedChanges) {
|
||||||
|
drawLine(
|
||||||
|
color = colors[itemPosition % colors.size],
|
||||||
|
start = Offset(20f * (itemPosition + 1), this.center.y),
|
||||||
|
end = Offset(20f * (itemPosition + 1), 0f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
forkingOffLanes.forEach { plotLane ->
|
||||||
|
drawLine(
|
||||||
|
color = colors[plotLane.position % colors.size],
|
||||||
|
start = Offset(20f * (itemPosition + 1), this.center.y),
|
||||||
|
end = Offset(20f * (plotLane.position + 1), 0f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
mergingLanes.forEach { plotLane ->
|
||||||
|
drawLine(
|
||||||
|
color = colors[plotLane.position % colors.size],
|
||||||
|
start = Offset(20f * (plotLane.position + 1), this.size.height),
|
||||||
|
end = Offset(20f * (itemPosition + 1), this.center.y),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plotCommit.parentCount > 0) {
|
||||||
|
drawLine(
|
||||||
|
color = colors[itemPosition % colors.size],
|
||||||
|
start = Offset(20f * (itemPosition + 1), this.center.y),
|
||||||
|
end = Offset(20f * (itemPosition + 1), this.size.height),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
passingLanes.forEach { plotLane ->
|
||||||
|
drawLine(
|
||||||
|
color = colors[plotLane.position % colors.size],
|
||||||
|
start = Offset(20f * (plotLane.position + 1), 0f),
|
||||||
|
end = Offset(20f * (plotLane.position + 1), this.size.height),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
drawCircle(
|
||||||
|
color = colors[itemPosition % colors.size],
|
||||||
|
radius = 10f,
|
||||||
|
center = Offset(20f * (itemPosition + 1), this.center.y),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UncommitedChangesGraphLine(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
hasPreviousCommits: Boolean,
|
||||||
|
) {
|
||||||
|
Box(modifier = modifier) {
|
||||||
|
Canvas(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
|
clipRect {
|
||||||
|
|
||||||
|
if (hasPreviousCommits)
|
||||||
|
drawLine(
|
||||||
|
color = colors[0],
|
||||||
|
start = Offset(20f, this.center.y),
|
||||||
|
end = Offset(20f, this.size.height),
|
||||||
|
)
|
||||||
|
|
||||||
|
drawCircle(
|
||||||
|
color = colors[0],
|
||||||
|
radius = 10f,
|
||||||
|
center = Offset(20f, this.center.y),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue