diff --git a/src/main/kotlin/Branches.kt b/src/main/kotlin/Branches.kt new file mode 100644 index 0000000..a073b69 --- /dev/null +++ b/src/main/kotlin/Branches.kt @@ -0,0 +1,108 @@ +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Build +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.res.useResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import extensions.filePath +import extensions.icon +import extensions.toByteArray +import org.eclipse.jgit.lib.Ref +import org.jetbrains.skija.Image + +@Composable +fun Branches(gitManager: GitManager) { + val branches by gitManager.branches.collectAsState() + val branchIcon = remember { + useResource("branch.png") { + Image.makeFromEncoded(it.toByteArray()).asImageBitmap() + } + } + Card( + modifier = Modifier + .fillMaxSize() + .padding(8.dp) + ) { + Column { + Text( + modifier = Modifier + .padding(vertical = 16.dp) + .fillMaxWidth(), + text = "Branches", + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + color = MaterialTheme.colors.primary, + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + LazyColumn(modifier = Modifier.weight(5f)) { + itemsIndexed(branches) { _, branch -> + BranchRow( + branch = branch, + icon = branchIcon, + ) + + } + } + } + } +} + +@Composable +private fun BranchRow(branch: Ref, icon: ImageBitmap) { + Row( + modifier = Modifier + .height(56.dp) + .fillMaxWidth() + .clickable(onClick = {}), + verticalAlignment = Alignment.CenterVertically, + ) { + + Icon( + bitmap = icon, + contentDescription = null, + modifier = Modifier + .padding(horizontal = 16.dp) + .size(24.dp), + tint = MaterialTheme.colors.primary, + ) + + Text( + text = branch.name, + modifier = Modifier.weight(1f, fill = true), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + + IconButton( + onClick = {}, + modifier = Modifier + .padding(horizontal = 16.dp) + .size(32.dp) + ) { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = null, + tint = MaterialTheme.colors.primary, + ) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/GitManager.kt b/src/main/kotlin/GitManager.kt index 0fe83a7..3cf8ce3 100644 --- a/src/main/kotlin/GitManager.kt +++ b/src/main/kotlin/GitManager.kt @@ -9,6 +9,7 @@ import org.eclipse.jgit.api.Git import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.diff.DiffFormatter import org.eclipse.jgit.dircache.DirCacheIterator +import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Repository import org.eclipse.jgit.storage.file.FileRepositoryBuilder import org.eclipse.jgit.treewalk.FileTreeIterator @@ -21,6 +22,7 @@ class GitManager { private val statusManager = StatusManager() private val logManager = LogManager() private val remoteOperationsManager = RemoteOperationsManager() + private val branchesManager = BranchesManager() private val managerScope = CoroutineScope(SupervisorJob()) @@ -38,6 +40,9 @@ class GitManager { val logStatus: StateFlow get() = logManager.logStatus + val branches: StateFlow> + get() = branchesManager.branches + val latestDirectoryOpened: File? get() = File(preferences.latestOpenedRepositoryPath).parentFile @@ -87,7 +92,7 @@ class GitManager { _repositorySelectionStatus.value = RepositorySelectionStatus.Open(repository) git = Git(repository) - loadLog() + refreshRepositoryInfo() } catch (ex: Exception) { ex.printStackTrace() } @@ -132,7 +137,7 @@ class GitManager { val newTree = FileTreeIterator(repo) println(diffEntry) - formatter.scan(oldTree, newTree) + formatter.scan(oldTree, newTree) //TODO Should only be set when using diff for unstaged changes // formatter.format(oldTree, newTree) formatter.format(diffEntry) formatter.flush() @@ -148,6 +153,11 @@ class GitManager { fun push() = managerScope.launch { remoteOperationsManager.push(safeGit) } + + private fun refreshRepositoryInfo() = managerScope.launch { + branchesManager.loadBranches(safeGit) + loadLog() + } } diff --git a/src/main/kotlin/Log.kt b/src/main/kotlin/Log.kt new file mode 100644 index 0000000..c20cbc8 --- /dev/null +++ b/src/main/kotlin/Log.kt @@ -0,0 +1,122 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material.Card +import androidx.compose.material.Divider +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import git.LogStatus +import org.eclipse.jgit.revwalk.RevCommit +import theme.primaryTextColor +import theme.secondaryTextColor + +@Composable +fun Log( + gitManager: GitManager, + onRevCommitSelected: (RevCommit) -> Unit, + onUncommitedChangesSelected: () -> Unit, + selectedIndex: MutableState = remember { mutableStateOf(-1) } +) { + val logStatusState = gitManager.logStatus.collectAsState() + val logStatus = logStatusState.value + + val selectedUncommited = remember { mutableStateOf(false) } + + val log = if (logStatus is LogStatus.Loaded) { + logStatus.commits + } else + listOf() + + Card( + modifier = Modifier + .padding(8.dp) + .background(MaterialTheme.colors.surface) + .fillMaxSize() + ) { + LazyColumn( + modifier = Modifier + .background(MaterialTheme.colors.surface) + .fillMaxSize() + ) { + if (gitManager.hasUncommitedChanges()) + item { + val textColor = if (selectedUncommited.value) { + MaterialTheme.colors.primary + } else + MaterialTheme.colors.primaryTextColor + + Column( + modifier = Modifier + .height(64.dp) + .fillMaxWidth() + .clickable { + selectedIndex.value = -1 + selectedUncommited.value = true + onUncommitedChangesSelected() + }, + verticalArrangement = Arrangement.Center, + ) { + Spacer(modifier = Modifier.weight(2f)) + + Text( + text = "Uncommited changes", + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(start = 16.dp), + color = textColor, + ) + Text( + text = "You", + modifier = Modifier.padding(start = 16.dp), + color = MaterialTheme.colors.secondaryTextColor, + ) + + Spacer(modifier = Modifier.weight(2f)) + + Divider() + } + } + + itemsIndexed(items = log) { index, item -> + val textColor = if (selectedIndex.value == index) { + MaterialTheme.colors.primary + } else + MaterialTheme.colors.primaryTextColor + + Column( + modifier = Modifier + .height(64.dp) + .fillMaxWidth() + .clickable { + selectedIndex.value = index + selectedUncommited.value = false + onRevCommitSelected(item) + }, + verticalArrangement = Arrangement.Center, + ) { + Spacer(modifier = Modifier.weight(2f)) + + Text( + text = item.shortMessage, + modifier = Modifier.padding(start = 16.dp), + color = textColor, + ) + Text( + text = item.authorIdent.name, + modifier = Modifier.padding(start = 16.dp), + color = MaterialTheme.colors.secondaryTextColor, + ) + Spacer(modifier = Modifier.weight(2f)) + + Divider() + } + } + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/RepositorySelected.kt b/src/main/kotlin/RepositorySelected.kt index 961962b..25ae1d6 100644 --- a/src/main/kotlin/RepositorySelected.kt +++ b/src/main/kotlin/RepositorySelected.kt @@ -1,21 +1,18 @@ import androidx.compose.animation.Crossfade -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.material.* +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import extensions.filePath -import git.LogStatus import org.eclipse.jgit.api.Git import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.lib.Repository @@ -24,9 +21,7 @@ import org.eclipse.jgit.revwalk.RevTree import org.eclipse.jgit.revwalk.RevWalk import org.eclipse.jgit.treewalk.AbstractTreeIterator import org.eclipse.jgit.treewalk.CanonicalTreeParser -import theme.backgroundColorLight import theme.primaryTextColor -import theme.secondaryTextColor import java.io.IOException @Composable @@ -45,6 +40,13 @@ fun RepositorySelected(gitManager: GitManager, repository: Repository) { val selectedIndexCommitLog = remember { mutableStateOf(-1) } Row { + Box( + modifier = Modifier + .weight(0.15f) + .fillMaxHeight() + ) { + Branches(gitManager = gitManager) + } Box( modifier = Modifier .weight(0.7f) @@ -94,7 +96,7 @@ fun RepositorySelected(gitManager: GitManager, repository: Repository) { } Box( modifier = Modifier - .weight(0.3f) + .weight(0.15f) .fillMaxHeight() ) { if (uncommitedChangesSelected) { @@ -119,6 +121,8 @@ fun RepositorySelected(gitManager: GitManager, repository: Repository) { } } + + @Composable fun DiffView(gitManager: GitManager, diffEntry: DiffEntry, onCloseDiffView: () -> Unit) { val text = remember(diffEntry) { @@ -169,109 +173,4 @@ fun prepareTreeParser(repository: Repository, commit: RevCommit): AbstractTreeIt walk.dispose() return treeParser } -} - -@Composable -fun Log( - gitManager: GitManager, - onRevCommitSelected: (RevCommit) -> Unit, - onUncommitedChangesSelected: () -> Unit, - selectedIndex: MutableState = remember { mutableStateOf(-1) } -) { - val logStatusState = gitManager.logStatus.collectAsState() - val logStatus = logStatusState.value - - val selectedUncommited = remember { mutableStateOf(false) } - - val log = if (logStatus is LogStatus.Loaded) { - logStatus.commits - } else - listOf() - - Card( - modifier = Modifier - .padding(8.dp) - .background(MaterialTheme.colors.surface) - .fillMaxSize() - ) { - LazyColumn( - modifier = Modifier - .background(MaterialTheme.colors.surface) - .fillMaxSize() - ) { - if (gitManager.hasUncommitedChanges()) - item { - val textColor = if (selectedUncommited.value) { - MaterialTheme.colors.primary - } else - MaterialTheme.colors.primaryTextColor - - Column( - modifier = Modifier - .height(64.dp) - .fillMaxWidth() - .clickable { - selectedIndex.value = -1 - selectedUncommited.value = true - onUncommitedChangesSelected() - }, - verticalArrangement = Arrangement.Center, - ) { - Spacer(modifier = Modifier.weight(2f)) - - Text( - text = "Uncommited changes", - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(start = 16.dp), - color = textColor, - ) - Text( - text = "You", - modifier = Modifier.padding(start = 16.dp), - color = MaterialTheme.colors.secondaryTextColor, - ) - - Spacer(modifier = Modifier.weight(2f)) - - Divider() - } - } - - itemsIndexed(items = log) { index, item -> - val textColor = if (selectedIndex.value == index) { - MaterialTheme.colors.primary - } else - MaterialTheme.colors.primaryTextColor - - Column( - modifier = Modifier - .height(64.dp) - .fillMaxWidth() - .clickable { - selectedIndex.value = index - selectedUncommited.value = false - onRevCommitSelected(item) - }, - verticalArrangement = Arrangement.Center, - ) { - Spacer(modifier = Modifier.weight(2f)) - - Text( - text = item.shortMessage, - modifier = Modifier.padding(start = 16.dp), - color = textColor, - ) - Text( - text = item.authorIdent.name, - modifier = Modifier.padding(start = 16.dp), - color = MaterialTheme.colors.secondaryTextColor, - ) - Spacer(modifier = Modifier.weight(2f)) - - Divider() - } - } - } - } - } \ No newline at end of file diff --git a/src/main/kotlin/git/BranchesManager.kt b/src/main/kotlin/git/BranchesManager.kt new file mode 100644 index 0000000..00cd2e8 --- /dev/null +++ b/src/main/kotlin/git/BranchesManager.kt @@ -0,0 +1,22 @@ +package git + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.withContext +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.Ref + +class BranchesManager { + private val _branches = MutableStateFlow>(listOf()) + val branches: StateFlow> + get() = _branches + + suspend fun loadBranches(git: Git) = withContext(Dispatchers.IO) { + val branchList = git + .branchList() + .call() + + _branches.value = branchList + } +} \ No newline at end of file diff --git a/src/main/resources/branch.png b/src/main/resources/branch.png new file mode 100644 index 0000000..52af568 Binary files /dev/null and b/src/main/resources/branch.png differ