Replaced nested SplitPane with a custom TripleVerticalSplit

This commit is contained in:
Abdelilah El Aissaoui 2024-01-09 21:18:32 +01:00
parent de64d43c15
commit d06e746c6c
No known key found for this signature in database
GPG Key ID: 7587FC860F594869
2 changed files with 191 additions and 145 deletions

View File

@ -12,9 +12,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.unit.dp
import com.jetpackduba.gitnuro.AppConstants
import com.jetpackduba.gitnuro.LocalTabScope
@ -24,6 +22,7 @@ import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
import com.jetpackduba.gitnuro.keybindings.matchesBinding
import com.jetpackduba.gitnuro.ui.components.SecondaryButton
import com.jetpackduba.gitnuro.ui.components.TripleVerticalSplit
import com.jetpackduba.gitnuro.ui.components.gitnuroDynamicViewModel
import com.jetpackduba.gitnuro.ui.dialogs.*
import com.jetpackduba.gitnuro.ui.diff.Diff
@ -33,9 +32,6 @@ import com.jetpackduba.gitnuro.viewmodels.TabViewModel
import org.eclipse.jgit.lib.RepositoryState
import org.eclipse.jgit.revwalk.RevCommit
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
import org.jetbrains.compose.splitpane.HorizontalSplitPane
import org.jetbrains.compose.splitpane.SplitterScope
import org.jetbrains.compose.splitpane.rememberSplitPaneState
@Composable
fun RepositoryOpenPage(
@ -244,7 +240,6 @@ fun RepoContent(
}
}
@OptIn(ExperimentalSplitPaneApi::class)
@Composable
fun MainContentView(
tabViewModel: TabViewModel,
@ -255,158 +250,125 @@ fun MainContentView(
) {
val rebaseInteractiveState by tabViewModel.rebaseInteractiveState.collectAsState()
HorizontalSplitPane(
splitPaneState = rememberSplitPaneState(initialPositionPercentage = 0.20f)
) {
val size = if (rebaseInteractiveState == RebaseInteractiveState.AwaitingInteraction)
1.dp
else
180.dp
first(minSize = size) {
TripleVerticalSplit(
modifier = Modifier.fillMaxSize(),
initialFirstWidth = 220f,
minFirstWidth = 150f,
initialThirdWidth = 360f,
minThirdWidth = 180f,
first = {
SidePanel()
}
splitter {
this.repositorySplitter()
}
second {
HorizontalSplitPane(
splitPaneState = rememberSplitPaneState(0.9f)
},
second = {
Box(
modifier = Modifier
.fillMaxSize()
) {
first {
Box(
modifier = Modifier
.fillMaxSize()
) {
if (rebaseInteractiveState == RebaseInteractiveState.AwaitingInteraction && diffSelected == null) {
RebaseInteractive()
} else if (blameState is BlameState.Loaded && !blameState.isMinimized) {
Blame(
filePath = blameState.filePath,
blameResult = blameState.blameResult,
onClose = { tabViewModel.resetBlameState() },
onSelectCommit = { tabViewModel.selectCommit(it) }
)
} else {
Column {
Box(modifier = Modifier.weight(1f, true)) {
when (diffSelected) {
null -> {
Log(
selectedItem = selectedItem,
repositoryState = repositoryState,
)
}
else -> {
val diffViewModel = tabViewModel.diffViewModel
if (diffViewModel != null) {
Diff(
diffViewModel = diffViewModel,
onCloseDiffView = {
tabViewModel.newDiffSelected = null
}
)
}
}
}
if (rebaseInteractiveState == RebaseInteractiveState.AwaitingInteraction && diffSelected == null) {
RebaseInteractive()
} else if (blameState is BlameState.Loaded && !blameState.isMinimized) {
Blame(
filePath = blameState.filePath,
blameResult = blameState.blameResult,
onClose = { tabViewModel.resetBlameState() },
onSelectCommit = { tabViewModel.selectCommit(it) }
)
} else {
Column {
Box(modifier = Modifier.weight(1f, true)) {
when (diffSelected) {
null -> {
Log(
selectedItem = selectedItem,
repositoryState = repositoryState,
)
}
if (blameState is BlameState.Loaded) { // BlameState.isMinimized is true here
MinimizedBlame(
filePath = blameState.filePath,
onExpand = { tabViewModel.expandBlame() },
onClose = { tabViewModel.resetBlameState() }
)
else -> {
val diffViewModel = tabViewModel.diffViewModel
if (diffViewModel != null) {
Diff(
diffViewModel = diffViewModel,
onCloseDiffView = {
tabViewModel.newDiffSelected = null
}
)
}
}
}
}
}
}
splitter {
this.repositorySplitter()
}
second(minSize = 250.dp) {
Box(
modifier = Modifier
.fillMaxHeight()
) {
when (selectedItem) {
SelectedItem.UncommittedChanges -> {
UncommittedChanges(
selectedEntryType = diffSelected,
repositoryState = repositoryState,
onStagedDiffEntrySelected = { diffEntry ->
tabViewModel.minimizeBlame()
tabViewModel.newDiffSelected = if (diffEntry != null) {
if (repositoryState == RepositoryState.SAFE)
DiffEntryType.SafeStagedDiff(diffEntry)
else
DiffEntryType.UnsafeStagedDiff(diffEntry)
} else {
null
}
},
onUnstagedDiffEntrySelected = { diffEntry ->
tabViewModel.minimizeBlame()
if (repositoryState == RepositoryState.SAFE)
tabViewModel.newDiffSelected = DiffEntryType.SafeUnstagedDiff(diffEntry)
else
tabViewModel.newDiffSelected = DiffEntryType.UnsafeUnstagedDiff(diffEntry)
},
onBlameFile = { tabViewModel.blameFile(it) },
onHistoryFile = { tabViewModel.fileHistory(it) }
)
}
is SelectedItem.CommitBasedItem -> {
CommitChanges(
selectedItem = selectedItem,
diffSelected = diffSelected,
onDiffSelected = { diffEntry ->
tabViewModel.minimizeBlame()
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
},
onBlame = { tabViewModel.blameFile(it) },
onHistory = { tabViewModel.fileHistory(it) },
)
}
SelectedItem.None -> {}
if (blameState is BlameState.Loaded) { // BlameState.isMinimized is true here
MinimizedBlame(
filePath = blameState.filePath,
onExpand = { tabViewModel.expandBlame() },
onClose = { tabViewModel.resetBlameState() }
)
}
}
}
}
}
}
}
},
third = {
Box(
modifier = Modifier
.fillMaxHeight()
) {
when (selectedItem) {
SelectedItem.UncommittedChanges -> {
UncommittedChanges(
selectedEntryType = diffSelected,
repositoryState = repositoryState,
onStagedDiffEntrySelected = { diffEntry ->
tabViewModel.minimizeBlame()
fun SplitterScope.repositorySplitter() {
visiblePart {
Box(
Modifier
.width(8.dp)
.fillMaxHeight()
.background(Color.Transparent)
)
}
handle {
Box(
Modifier
.markAsHandle()
.pointerHoverIcon(resizePointerIconEast)
.background(Color.Transparent)
.width(8.dp)
.fillMaxHeight()
)
}
tabViewModel.newDiffSelected = if (diffEntry != null) {
if (repositoryState == RepositoryState.SAFE)
DiffEntryType.SafeStagedDiff(diffEntry)
else
DiffEntryType.UnsafeStagedDiff(diffEntry)
} else {
null
}
},
onUnstagedDiffEntrySelected = { diffEntry ->
tabViewModel.minimizeBlame()
if (repositoryState == RepositoryState.SAFE)
tabViewModel.newDiffSelected = DiffEntryType.SafeUnstagedDiff(diffEntry)
else
tabViewModel.newDiffSelected = DiffEntryType.UnsafeUnstagedDiff(diffEntry)
},
onBlameFile = { tabViewModel.blameFile(it) },
onHistoryFile = { tabViewModel.fileHistory(it) }
)
}
is SelectedItem.CommitBasedItem -> {
CommitChanges(
selectedItem = selectedItem,
diffSelected = diffSelected,
onDiffSelected = { diffEntry ->
tabViewModel.minimizeBlame()
tabViewModel.newDiffSelected = DiffEntryType.CommitDiff(diffEntry)
},
onBlame = { tabViewModel.blameFile(it) },
onHistory = { tabViewModel.fileHistory(it) },
)
}
SelectedItem.None -> {}
}
}
},
onFirstSizeChanged = {
},
onThirdSizeChanged = {
},
)
}
sealed interface SelectedItem {

View File

@ -0,0 +1,84 @@
package com.jetpackduba.gitnuro.ui.components
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import com.jetpackduba.gitnuro.ui.resizePointerIconEast
@Composable
fun TripleVerticalSplit(
modifier: Modifier = Modifier,
first: @Composable () -> Unit,
second: @Composable () -> Unit,
third: @Composable () -> Unit,
initialFirstWidth: Float,
minFirstWidth: Float,
initialThirdWidth: Float,
minThirdWidth: Float,
onFirstSizeChanged: (Float) -> Unit,
onThirdSizeChanged: (Float) -> Unit,
) {
val density = LocalDensity.current.density
var firstWidth by remember { mutableStateOf(initialFirstWidth) }
var thirdWidth by remember { mutableStateOf(initialThirdWidth) }
Row(
modifier = modifier
) {
Box(modifier = Modifier.width((firstWidth).dp)) {
first()
}
Box(
modifier = Modifier
.fillMaxHeight()
.width(8.dp)
.draggable(
state = rememberDraggableState {
val newWidth = firstWidth + it / density
if (newWidth > minFirstWidth) {
firstWidth = newWidth
onFirstSizeChanged(firstWidth)
}
},
orientation = Orientation.Horizontal
)
.pointerHoverIcon(resizePointerIconEast)
)
Box(Modifier.weight(1f, true)) {
second()
}
Box(
modifier = Modifier
.fillMaxHeight()
.width(8.dp)
.draggable(
rememberDraggableState {
val newWidth = thirdWidth - it / density
if(newWidth > minThirdWidth) {
thirdWidth = newWidth
onThirdSizeChanged(thirdWidth)
}
}, Orientation.Horizontal
)
.pointerHoverIcon(resizePointerIconEast)
)
Box(modifier = Modifier.width(thirdWidth.dp)) {
third()
}
}
}