Implemented close view with ESC

This commit is contained in:
Abdelilah El Aissaoui 2024-09-09 00:31:43 +02:00
parent 355cbc3f79
commit 1af4542c7c
No known key found for this signature in database
GPG Key ID: 7587FC860F594869
7 changed files with 135 additions and 52 deletions

View File

@ -12,6 +12,8 @@ import com.jetpackduba.gitnuro.models.Notification
import com.jetpackduba.gitnuro.ui.SelectedItem import com.jetpackduba.gitnuro.ui.SelectedItem
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.ObjectId import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref
@ -54,9 +56,10 @@ class TabState @Inject constructor(
} }
private val refreshData = MutableSharedFlow<RefreshType>() private val refreshData = MutableSharedFlow<RefreshType>()
private val closeableViews = ArrayDeque<Int>() private val closeableViews = ArrayDeque<CloseableView>()
private val closeableViewsMutex = Mutex()
private val _closeView = MutableSharedFlow<Int>() private val _closeView = MutableSharedFlow<CloseableView>()
val closeViewFlow = _closeView.asSharedFlow() val closeViewFlow = _closeView.asSharedFlow()
/** /**
@ -311,15 +314,16 @@ class TabState @Inject constructor(
} }
} }
fun addCloseableView(id: Int) { suspend fun addCloseableView(view: CloseableView): Unit = closeableViewsMutex.withLock {
closeableViews.add(id) closeableViews.remove(view) // Remove any previous elements if present
closeableViews.add(view)
} }
fun removeCloseableView(id: Int) { suspend fun removeCloseableView(view: CloseableView): Unit = closeableViewsMutex.withLock {
closeableViews.remove(id) closeableViews.remove(view)
} }
suspend fun closeLastView() { suspend fun closeLastView(): Unit = closeableViewsMutex.withLock {
val last = closeableViews.removeLastOrNull() val last = closeableViews.removeLastOrNull()
if (last != null) { if (last != null) {
@ -344,3 +348,8 @@ enum class RefreshType {
REMOTES, REMOTES,
REBASE_INTERACTIVE_STATE, REBASE_INTERACTIVE_STATE,
} }
enum class CloseableView {
DIFF,
LOG_SEARCH,
}

View File

@ -105,21 +105,22 @@ fun RepositoryOpenPage(
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
LaunchedEffect(selectedItem) {
focusRequester.requestFocus()
}
Column( Column(
modifier = Modifier.onPreviewKeyEvent { modifier = Modifier
println("Key event $it") .focusRequester(focusRequester)
.focusable(true)
.onPreviewKeyEvent {
when { when {
it.matchesBinding(KeybindingOption.PULL) -> { it.matchesBinding(KeybindingOption.PULL) -> {
repositoryOpenViewModel.pull(PullType.DEFAULT) repositoryOpenViewModel.pull(PullType.DEFAULT)
true true
} }
it.matchesBinding(KeybindingOption.PUSH) -> { it.matchesBinding(KeybindingOption.PUSH) -> {
repositoryOpenViewModel.push() repositoryOpenViewModel.push()
true true
} }
it.matchesBinding(KeybindingOption.BRANCH_CREATE) -> { it.matchesBinding(KeybindingOption.BRANCH_CREATE) -> {
if (!showNewBranchDialog) { if (!showNewBranchDialog) {
showNewBranchDialog = true showNewBranchDialog = true
@ -128,18 +129,22 @@ fun RepositoryOpenPage(
false false
} }
} }
it.matchesBinding(KeybindingOption.STASH) -> { it.matchesBinding(KeybindingOption.STASH) -> {
repositoryOpenViewModel.stash() repositoryOpenViewModel.stash()
true true
} }
it.matchesBinding(KeybindingOption.STASH_POP) -> { it.matchesBinding(KeybindingOption.STASH_POP) -> {
repositoryOpenViewModel.popStash() repositoryOpenViewModel.popStash()
true true
} }
it.matchesBinding(KeybindingOption.EXIT) -> { it.matchesBinding(KeybindingOption.EXIT) -> {
repositoryOpenViewModel.closeLastView() repositoryOpenViewModel.closeLastView()
true true
} }
else -> false else -> false
} }
@ -148,7 +153,6 @@ fun RepositoryOpenPage(
Row(modifier = Modifier.weight(1f)) { Row(modifier = Modifier.weight(1f)) {
Column( Column(
modifier = Modifier modifier = Modifier
.focusRequester(focusRequester)
.focusable() .focusable()
.onKeyEvent { keyEvent -> .onKeyEvent { keyEvent ->
if (keyEvent.matchesBinding(KeybindingOption.REFRESH)) { if (keyEvent.matchesBinding(KeybindingOption.REFRESH)) {
@ -211,6 +215,10 @@ fun RepositoryOpenPage(
onShowAuthorInfoDialog = { repositoryOpenViewModel.showAuthorInfoDialog() }, onShowAuthorInfoDialog = { repositoryOpenViewModel.showAuthorInfoDialog() },
) )
} }
LaunchedEffect(repositoryOpenViewModel) {
focusRequester.requestFocus()
}
} }
@Composable @Composable

View File

@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
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.focus.onFocusChanged
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
@ -38,6 +39,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.jetpackduba.gitnuro.AppIcons import com.jetpackduba.gitnuro.AppIcons
import com.jetpackduba.gitnuro.extensions.* import com.jetpackduba.gitnuro.extensions.*
import com.jetpackduba.gitnuro.git.CloseableView
import com.jetpackduba.gitnuro.git.DiffType import com.jetpackduba.gitnuro.git.DiffType
import com.jetpackduba.gitnuro.git.EntryContent import com.jetpackduba.gitnuro.git.EntryContent
import com.jetpackduba.gitnuro.git.animatedImages import com.jetpackduba.gitnuro.git.animatedImages
@ -62,6 +64,7 @@ import com.jetpackduba.gitnuro.viewmodels.DiffViewModel
import com.jetpackduba.gitnuro.viewmodels.TextDiffType import com.jetpackduba.gitnuro.viewmodels.TextDiffType
import com.jetpackduba.gitnuro.viewmodels.ViewDiffResult import com.jetpackduba.gitnuro.viewmodels.ViewDiffResult
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.diff.DiffEntry
import org.eclipse.jgit.submodule.SubmoduleStatusType import org.eclipse.jgit.submodule.SubmoduleStatusType
@ -93,12 +96,23 @@ fun Diff(
val viewDiffResult = diffResultState.value ?: return val viewDiffResult = diffResultState.value ?: return
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
diffViewModel.closeViewFlow.collectLatest {
if (it == CloseableView.DIFF) onCloseDiffView()
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.background(MaterialTheme.colors.background) .background(MaterialTheme.colors.background)
.fillMaxSize() .fillMaxSize()
.focusable() .focusable(true)
.focusRequester(focusRequester) .focusRequester(focusRequester)
.onFocusChanged {
if (it.isFocused) {
diffViewModel.addToCloseables()
}
}
.onPreviewKeyEvent { keyEvent -> .onPreviewKeyEvent { keyEvent ->
if (keyEvent.matchesBinding(KeybindingOption.EXIT) && keyEvent.type == KeyEventType.KeyDown) { if (keyEvent.matchesBinding(KeybindingOption.EXIT) && keyEvent.type == KeyEventType.KeyDown) {
onCloseDiffView() onCloseDiffView()

View File

@ -24,6 +24,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds 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.focus.onFocusChanged
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
@ -115,6 +116,7 @@ fun Log(
repositoryState = repositoryState, repositoryState = repositoryState,
changeDefaultUpstreamBranchViewModel = changeDefaultUpstreamBranchViewModel, changeDefaultUpstreamBranchViewModel = changeDefaultUpstreamBranchViewModel,
) )
LogStatus.Loading -> LogLoading() LogStatus.Loading -> LogLoading()
} }
} }
@ -204,8 +206,13 @@ private fun LogLoaded(
if (searchFilterValue is LogSearch.SearchResults) { if (searchFilterValue is LogSearch.SearchResults) {
SearchFilter(logViewModel, searchFilterValue) SearchFilter(
logViewModel = logViewModel,
searchFilterResults = searchFilterValue,
searchFocused = { logViewModel.addSearchToCloseableView() },
)
} }
GraphHeader( GraphHeader(
graphWidth = graphWidth, graphWidth = graphWidth,
onPaddingChange = { onPaddingChange = {
@ -353,7 +360,8 @@ suspend fun scrollToUncommittedChanges(
@Composable @Composable
fun SearchFilter( fun SearchFilter(
logViewModel: LogViewModel, logViewModel: LogViewModel,
searchFilterResults: LogSearch.SearchResults searchFilterResults: LogSearch.SearchResults,
searchFocused: () -> Unit,
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var searchFilterText by remember { mutableStateOf(logViewModel.savedSearchFilter) } var searchFilterText by remember { mutableStateOf(logViewModel.savedSearchFilter) }
@ -381,6 +389,11 @@ fun SearchFilter(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.focusRequester(textFieldFocusRequester) .focusRequester(textFieldFocusRequester)
.onFocusChanged {
if (it.isFocused) {
searchFocused()
}
}
.onPreviewKeyEvent { keyEvent -> .onPreviewKeyEvent { keyEvent ->
when { when {
keyEvent.matchesBinding(KeybindingOption.SIMPLE_ACCEPT) -> { keyEvent.matchesBinding(KeybindingOption.SIMPLE_ACCEPT) -> {

View File

@ -3,6 +3,7 @@ package com.jetpackduba.gitnuro.viewmodels
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import com.jetpackduba.gitnuro.exceptions.MissingDiffEntryException import com.jetpackduba.gitnuro.exceptions.MissingDiffEntryException
import com.jetpackduba.gitnuro.extensions.delayedStateChange import com.jetpackduba.gitnuro.extensions.delayedStateChange
import com.jetpackduba.gitnuro.git.CloseableView
import com.jetpackduba.gitnuro.git.DiffType import com.jetpackduba.gitnuro.git.DiffType
import com.jetpackduba.gitnuro.git.RefreshType import com.jetpackduba.gitnuro.git.RefreshType
import com.jetpackduba.gitnuro.git.TabState import com.jetpackduba.gitnuro.git.TabState
@ -38,10 +39,12 @@ class DiffViewModel @Inject constructor(
private val discardUnstagedHunkLineUseCase: DiscardUnstagedHunkLineUseCase, private val discardUnstagedHunkLineUseCase: DiscardUnstagedHunkLineUseCase,
private val tabsManager: TabsManager, private val tabsManager: TabsManager,
tabScope: CoroutineScope, tabScope: CoroutineScope,
) { ) : AutoCloseable {
private val _diffResult = MutableStateFlow<ViewDiffResult>(ViewDiffResult.Loading("")) private val _diffResult = MutableStateFlow<ViewDiffResult>(ViewDiffResult.Loading(""))
val diffResult: StateFlow<ViewDiffResult?> = _diffResult val diffResult: StateFlow<ViewDiffResult?> = _diffResult
val closeViewFlow = tabState.closeViewFlow
val diffTypeFlow = settings.textDiffTypeFlow val diffTypeFlow = settings.textDiffTypeFlow
val isDisplayFullFile = settings.diffDisplayFullFileFlow val isDisplayFullFile = settings.diffDisplayFullFileFlow
@ -92,6 +95,8 @@ class DiffViewModel @Inject constructor(
) )
fun updateDiff(diffType: DiffType) { fun updateDiff(diffType: DiffType) {
addToCloseables()
diffJob = tabState.runOperation(refreshType = RefreshType.NONE) { git -> diffJob = tabState.runOperation(refreshType = RefreshType.NONE) { git ->
this.diffType = diffType this.diffType = diffType
@ -223,6 +228,19 @@ class DiffViewModel @Inject constructor(
fun openSubmodule(path: String) = tabState.runOperation(refreshType = RefreshType.NONE) { git -> fun openSubmodule(path: String) = tabState.runOperation(refreshType = RefreshType.NONE) { git ->
tabsManager.addNewTabFromPath("${git.repository.workTree}/$path", true) tabsManager.addNewTabFromPath("${git.repository.workTree}/$path", true)
} }
fun addToCloseables() = tabState.runOperation(refreshType = RefreshType.NONE) { _ ->
tabState.addCloseableView(CloseableView.DIFF)
}
private fun removeFromCloseables() = tabState.runOperation(refreshType = RefreshType.NONE) { _ ->
tabState.removeCloseableView(CloseableView.DIFF)
}
override fun close() {
cancelRunningJobs()
removeFromCloseables()
}
} }
enum class TextDiffType(val value: Int) { enum class TextDiffType(val value: Int) {

View File

@ -5,6 +5,7 @@ import androidx.compose.foundation.lazy.LazyListState
import com.jetpackduba.gitnuro.TaskType import com.jetpackduba.gitnuro.TaskType
import com.jetpackduba.gitnuro.extensions.delayedStateChange import com.jetpackduba.gitnuro.extensions.delayedStateChange
import com.jetpackduba.gitnuro.extensions.shortName import com.jetpackduba.gitnuro.extensions.shortName
import com.jetpackduba.gitnuro.git.CloseableView
import com.jetpackduba.gitnuro.git.RefreshType import com.jetpackduba.gitnuro.git.RefreshType
import com.jetpackduba.gitnuro.git.TabState import com.jetpackduba.gitnuro.git.TabState
import com.jetpackduba.gitnuro.git.TaskEvent import com.jetpackduba.gitnuro.git.TaskEvent
@ -124,6 +125,14 @@ class LogViewModel @Inject constructor(
refresh(tabState.git) refresh(tabState.git)
} }
} }
tabScope.launch {
tabState.closeViewFlow.collectLatest {
if (it == CloseableView.LOG_SEARCH) {
_logSearchFilterResults.value = LogSearch.NotSearching
}
}
}
} }
@ -325,8 +334,11 @@ class LogViewModel @Inject constructor(
} }
_logSearchFilterResults.value = LogSearch.SearchResults(matchingCommits, startingUiIndex) _logSearchFilterResults.value = LogSearch.SearchResults(matchingCommits, startingUiIndex)
} else addSearchToCloseableView()
} else {
_logSearchFilterResults.value = LogSearch.SearchResults(emptyList(), NONE_MATCHING_INDEX) _logSearchFilterResults.value = LogSearch.SearchResults(emptyList(), NONE_MATCHING_INDEX)
addSearchToCloseableView()
}
} }
suspend fun selectPreviousFilterCommit() { suspend fun selectPreviousFilterCommit() {
@ -347,6 +359,7 @@ class LogViewModel @Inject constructor(
_logSearchFilterResults.value = logSearchFilterResultsValue.copy(index = newIndex) _logSearchFilterResults.value = logSearchFilterResultsValue.copy(index = newIndex)
_focusCommit.emit(newCommitToSelect) _focusCommit.emit(newCommitToSelect)
addSearchToCloseableView()
} }
suspend fun selectNextFilterCommit() { suspend fun selectNextFilterCommit() {
@ -369,6 +382,7 @@ class LogViewModel @Inject constructor(
_logSearchFilterResults.value = logSearchFilterResultsValue.copy(index = newIndex) _logSearchFilterResults.value = logSearchFilterResultsValue.copy(index = newIndex)
_focusCommit.emit(newCommitToSelect) _focusCommit.emit(newCommitToSelect)
addSearchToCloseableView()
} }
fun showDialog(dialog: LogDialog) { fun showDialog(dialog: LogDialog) {
@ -377,6 +391,15 @@ class LogViewModel @Inject constructor(
fun closeSearch() { fun closeSearch() {
_logSearchFilterResults.value = LogSearch.NotSearching _logSearchFilterResults.value = LogSearch.NotSearching
removeSearchFromCloseableView()
}
fun addSearchToCloseableView() = tabState.runOperation(refreshType = RefreshType.NONE) { _ ->
tabState.addCloseableView(CloseableView.LOG_SEARCH)
}
private fun removeSearchFromCloseableView() = tabState.runOperation(refreshType = RefreshType.NONE) { _ ->
tabState.removeCloseableView(CloseableView.LOG_SEARCH)
} }
fun rebaseInteractive(revCommit: RevCommit) = tabState.safeProcessing( fun rebaseInteractive(revCommit: RevCommit) = tabState.safeProcessing(

View File

@ -122,8 +122,6 @@ class RepositoryOpenViewModel @Inject constructor(
launch { launch {
watchRepositoryChanges(tabState.git) watchRepositoryChanges(tabState.git)
} }
launch { tabState.refreshData(RefreshType.ALL_DATA) }
} }
} }
@ -252,7 +250,7 @@ class RepositoryOpenViewModel @Inject constructor(
diffViewModel?.cancelRunningJobs() diffViewModel?.cancelRunningJobs()
diffViewModel?.updateDiff(diffSelected) diffViewModel?.updateDiff(diffSelected)
} else { } else {
diffViewModel?.cancelRunningJobs() diffViewModel?.close()
diffViewModel = null // Free the view model from the memory if not being used. diffViewModel = null // Free the view model from the memory if not being used.
} }
} }