Width of triple vertical split pane columns is now persisted

Fixes #176
This commit is contained in:
Abdelilah El Aissaoui 2024-07-20 01:18:39 +02:00
parent b9db9a8d57
commit f5ee173453
No known key found for this signature in database
GPG Key ID: 7587FC860F594869
6 changed files with 129 additions and 17 deletions

View File

@ -13,6 +13,7 @@ import com.jetpackduba.gitnuro.managers.TempFilesManager
import com.jetpackduba.gitnuro.repositories.AppSettingsRepository import com.jetpackduba.gitnuro.repositories.AppSettingsRepository
import com.jetpackduba.gitnuro.terminal.ITerminalProvider import com.jetpackduba.gitnuro.terminal.ITerminalProvider
import com.jetpackduba.gitnuro.ui.TabsManager import com.jetpackduba.gitnuro.ui.TabsManager
import com.jetpackduba.gitnuro.ui.VerticalSplitPaneConfig
import com.jetpackduba.gitnuro.updates.UpdatesRepository import com.jetpackduba.gitnuro.updates.UpdatesRepository
import com.jetpackduba.gitnuro.viewmodels.SettingsViewModel import com.jetpackduba.gitnuro.viewmodels.SettingsViewModel
import dagger.Component import dagger.Component
@ -34,6 +35,8 @@ interface AppComponent {
fun appPreferences(): AppSettingsRepository fun appPreferences(): AppSettingsRepository
fun verticalSplitPaneConfig(): VerticalSplitPaneConfig
fun appEnvInfo(): AppEnvInfo fun appEnvInfo(): AppEnvInfo
fun tabsManager(): TabsManager fun tabsManager(): TabsManager

View File

@ -43,6 +43,8 @@ private const val PREF_PROXY_USE_AUTH = "proxyUseAuth"
private const val PREF_PROXY_USER = "proxyHostUser" private const val PREF_PROXY_USER = "proxyHostUser"
private const val PREF_PROXY_PASSWORD = "proxyHostPassword" private const val PREF_PROXY_PASSWORD = "proxyHostPassword"
private const val PREF_CACHE_CREDENTIALS_IN_MEMORY = "credentialsInMemory" private const val PREF_CACHE_CREDENTIALS_IN_MEMORY = "credentialsInMemory"
private const val PREF_FIRST_PANE_WIDTH = "firstPaneWidth"
private const val PREF_THIRD_PANE_WIDTH = "thirdPaneWidth"
private const val PREF_GIT_FF_MERGE = "gitFFMerge" private const val PREF_GIT_FF_MERGE = "gitFFMerge"
@ -57,6 +59,8 @@ private const val DEFAULT_SWAP_UNCOMMITTED_CHANGES = false
private const val DEFAULT_SHOW_CHANGES_AS_TREE = false private const val DEFAULT_SHOW_CHANGES_AS_TREE = false
private const val DEFAULT_CACHE_CREDENTIALS_IN_MEMORY = true private const val DEFAULT_CACHE_CREDENTIALS_IN_MEMORY = true
private const val DEFAULT_VERIFY_SSL = true private const val DEFAULT_VERIFY_SSL = true
private const val DEFAULT_FIRST_PANE_WIDTH = 220f
private const val DEFAULT_THIRD_PANE_WIDTH = 330f
const val DEFAULT_UI_SCALE = -1f const val DEFAULT_UI_SCALE = -1f
@Singleton @Singleton
@ -209,6 +213,22 @@ class AppSettingsRepository @Inject constructor() {
_cacheCredentialsInMemoryFlow.value = value _cacheCredentialsInMemoryFlow.value = value
} }
var firstPaneWidth: Float
get() {
return preferences.getFloat(PREF_FIRST_PANE_WIDTH, DEFAULT_FIRST_PANE_WIDTH)
}
set(value) {
preferences.putFloat(PREF_FIRST_PANE_WIDTH, value)
}
var thirdPaneWidth: Float
get() {
return preferences.getFloat(PREF_THIRD_PANE_WIDTH, DEFAULT_THIRD_PANE_WIDTH)
}
set(value) {
preferences.putFloat(PREF_THIRD_PANE_WIDTH, value)
}
var verifySsl: Boolean var verifySsl: Boolean
get() { get() {
return preferences.getBoolean(PREF_VERIFY_SSL, DEFAULT_VERIFY_SSL) return preferences.getBoolean(PREF_VERIFY_SSL, DEFAULT_VERIFY_SSL)

View File

@ -30,6 +30,8 @@ import com.jetpackduba.gitnuro.ui.log.Log
import com.jetpackduba.gitnuro.updates.Update import com.jetpackduba.gitnuro.updates.Update
import com.jetpackduba.gitnuro.viewmodels.BlameState import com.jetpackduba.gitnuro.viewmodels.BlameState
import com.jetpackduba.gitnuro.viewmodels.TabViewModel import com.jetpackduba.gitnuro.viewmodels.TabViewModel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.eclipse.jgit.lib.RepositoryState import org.eclipse.jgit.lib.RepositoryState
import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.revwalk.RevCommit
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
@ -264,9 +266,19 @@ fun MainContentView(
) { ) {
val rebaseInteractiveState by tabViewModel.rebaseInteractiveState.collectAsState() val rebaseInteractiveState by tabViewModel.rebaseInteractiveState.collectAsState()
val density = LocalDensity.current.density val density = LocalDensity.current.density
val scope = rememberCoroutineScope()
var firstWidth by remember(tabViewModel) { mutableStateOf(tabViewModel.firstPaneWidth) } // We create 2 mutableStates here because using directly the flow makes compose lose some drag events for some reason
var thirdWidth by remember(tabViewModel) { mutableStateOf(tabViewModel.thirdPaneWidth) } var firstWidth by remember(tabViewModel) { mutableStateOf(tabViewModel.firstPaneWidth.value) }
var thirdWidth by remember(tabViewModel) { mutableStateOf(tabViewModel.thirdPaneWidth.value) }
LaunchedEffect(Unit) {
// Update the pane widths if they have been changed in a different tab
tabViewModel.onPanelsWidthPersisted.collectLatest {
firstWidth = tabViewModel.firstPaneWidth.value
thirdWidth = tabViewModel.thirdPaneWidth.value
}
}
TripleVerticalSplitPanel( TripleVerticalSplitPanel(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@ -391,7 +403,12 @@ fun MainContentView(
if (newWidth > 150 && rebaseInteractiveState !is RebaseInteractiveState.AwaitingInteraction) { if (newWidth > 150 && rebaseInteractiveState !is RebaseInteractiveState.AwaitingInteraction) {
firstWidth = newWidth firstWidth = newWidth
tabViewModel.firstPaneWidth = firstWidth tabViewModel.setFirstPaneWidth(newWidth)
}
},
onFirstSizeDragStopped = {
scope.launch {
tabViewModel.persistFirstPaneWidth()
} }
}, },
onThirdSizeDrag = { onThirdSizeDrag = {
@ -399,7 +416,12 @@ fun MainContentView(
if (newWidth > 150) { if (newWidth > 150) {
thirdWidth = newWidth thirdWidth = newWidth
tabViewModel.thirdPaneWidth = thirdWidth tabViewModel.setThirdPaneWidth(newWidth)
}
},
onThirdSizeDragStopped = {
scope.launch {
tabViewModel.persistThirdPaneWidth()
} }
}, },
) )

View File

@ -0,0 +1,51 @@
package com.jetpackduba.gitnuro.ui
import com.jetpackduba.gitnuro.repositories.AppSettingsRepository
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton
interface IVerticalSplitPaneConfig {
val firstPaneWidth: StateFlow<Float>
val onPanelsWidthPersisted: SharedFlow<Unit>
val thirdPaneWidth: StateFlow<Float>
fun setFirstPaneWidth(firstPaneWidth: Float)
fun setThirdPaneWidth(thirdPaneWidth: Float)
suspend fun persistFirstPaneWidth()
suspend fun persistThirdPaneWidth()
}
@Singleton
class VerticalSplitPaneConfig @Inject constructor(
private val appSettingsRepository: AppSettingsRepository,
) : IVerticalSplitPaneConfig {
private val _firstPaneWidth = MutableStateFlow<Float>(appSettingsRepository.firstPaneWidth)
override val firstPaneWidth: StateFlow<Float> = _firstPaneWidth
private val _thirdPaneWidth = MutableStateFlow<Float>(appSettingsRepository.thirdPaneWidth)
override val thirdPaneWidth: StateFlow<Float> = _thirdPaneWidth
private val _onPanelsWidthPersisted = MutableSharedFlow<Unit>()
override val onPanelsWidthPersisted: SharedFlow<Unit> = _onPanelsWidthPersisted
override fun setFirstPaneWidth(firstPaneWidth: Float) {
this._firstPaneWidth.value = firstPaneWidth
}
override fun setThirdPaneWidth(thirdPaneWidth: Float) {
this._thirdPaneWidth.value = thirdPaneWidth
}
override suspend fun persistFirstPaneWidth() {
appSettingsRepository.firstPaneWidth = _firstPaneWidth.value
_onPanelsWidthPersisted.emit(Unit)
}
override suspend fun persistThirdPaneWidth() {
appSettingsRepository.thirdPaneWidth = _thirdPaneWidth.value
_onPanelsWidthPersisted.emit(Unit)
}
}

View File

@ -3,10 +3,7 @@ package com.jetpackduba.gitnuro.ui.components
import androidx.compose.foundation.gestures.Orientation 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.Box import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerHoverIcon import androidx.compose.ui.input.pointer.pointerHoverIcon
@ -22,13 +19,18 @@ fun TripleVerticalSplitPanel(
firstWidth: Float, firstWidth: Float,
thirdWidth: Float, thirdWidth: Float,
onFirstSizeDrag: (Float) -> Unit, onFirstSizeDrag: (Float) -> Unit,
onFirstSizeDragStopped: (Float) -> Unit,
onThirdSizeDrag: (Float) -> Unit, onThirdSizeDrag: (Float) -> Unit,
onThirdSizeDragStopped: (Float) -> Unit,
) { ) {
Row( Row(
modifier = modifier modifier = modifier
) { ) {
if (firstWidth > 0) { if (firstWidth > 0) {
Box(modifier = Modifier.width(firstWidth.dp)) { Box(
modifier = Modifier
.width(firstWidth.dp)
) {
first() first()
} }
Box( Box(
@ -39,13 +41,19 @@ fun TripleVerticalSplitPanel(
state = rememberDraggableState { state = rememberDraggableState {
onFirstSizeDrag(it) onFirstSizeDrag(it)
}, },
orientation = Orientation.Horizontal orientation = Orientation.Horizontal,
onDragStopped = {
onFirstSizeDragStopped(it)
},
) )
.pointerHoverIcon(resizePointerIconEast) .pointerHoverIcon(resizePointerIconEast)
) )
} }
Box(Modifier.weight(1f, true)) { Box(
Modifier
.weight(1f, true)
) {
second() second()
} }
@ -54,14 +62,21 @@ fun TripleVerticalSplitPanel(
.fillMaxHeight() .fillMaxHeight()
.width(8.dp) .width(8.dp)
.draggable( .draggable(
rememberDraggableState { state = rememberDraggableState {
onThirdSizeDrag(it) onThirdSizeDrag(it)
}, Orientation.Horizontal },
orientation = Orientation.Horizontal,
onDragStopped = {
onThirdSizeDragStopped(it)
},
) )
.pointerHoverIcon(resizePointerIconEast) .pointerHoverIcon(resizePointerIconEast)
) )
Box(modifier = Modifier.width(thirdWidth.dp)) { Box(
modifier = Modifier
.width(thirdWidth.dp)
) {
third() third()
} }
} }

View File

@ -22,8 +22,10 @@ import com.jetpackduba.gitnuro.models.AuthorInfoSimple
import com.jetpackduba.gitnuro.system.OpenFilePickerUseCase import com.jetpackduba.gitnuro.system.OpenFilePickerUseCase
import com.jetpackduba.gitnuro.system.OpenUrlInBrowserUseCase import com.jetpackduba.gitnuro.system.OpenUrlInBrowserUseCase
import com.jetpackduba.gitnuro.system.PickerType import com.jetpackduba.gitnuro.system.PickerType
import com.jetpackduba.gitnuro.ui.IVerticalSplitPaneConfig
import com.jetpackduba.gitnuro.ui.SelectedItem import com.jetpackduba.gitnuro.ui.SelectedItem
import com.jetpackduba.gitnuro.ui.TabsManager import com.jetpackduba.gitnuro.ui.TabsManager
import com.jetpackduba.gitnuro.ui.VerticalSplitPaneConfig
import com.jetpackduba.gitnuro.updates.Update import com.jetpackduba.gitnuro.updates.Update
import com.jetpackduba.gitnuro.updates.UpdatesRepository import com.jetpackduba.gitnuro.updates.UpdatesRepository
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -69,10 +71,9 @@ class TabViewModel @Inject constructor(
private val sharedRepositoryStateManager: SharedRepositoryStateManager, private val sharedRepositoryStateManager: SharedRepositoryStateManager,
private val tabsManager: TabsManager, private val tabsManager: TabsManager,
private val tabScope: CoroutineScope, private val tabScope: CoroutineScope,
private val verticalSplitPaneConfig: VerticalSplitPaneConfig,
val tabViewModelsProvider: TabViewModelsProvider, val tabViewModelsProvider: TabViewModelsProvider,
) { ) : IVerticalSplitPaneConfig by verticalSplitPaneConfig {
var firstPaneWidth = 220f
var thirdPaneWidth = 360f
var initialPath: String? = null // Stores the path that should be opened when the tab is selected var initialPath: String? = null // Stores the path that should be opened when the tab is selected
val errorsManager: ErrorsManager = tabState.errorsManager val errorsManager: ErrorsManager = tabState.errorsManager
val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem