From e5a84705e918fdde53653a5e229ab3a437040c3f Mon Sep 17 00:00:00 2001 From: Abdelilah El Aissaoui Date: Mon, 9 Sep 2024 01:49:26 +0200 Subject: [PATCH] Improved focus handling in the whole tab --- .../com/jetpackduba/gitnuro/git/TabState.kt | 4 ++ .../jetpackduba/gitnuro/ui/CommitChanges.kt | 7 ++ .../jetpackduba/gitnuro/ui/RepositoryOpen.kt | 68 +++++++++++-------- .../com/jetpackduba/gitnuro/ui/SidePanel.kt | 29 +++++++- .../gitnuro/ui/UncommitedChanges.kt | 46 ++++++++++++- .../gitnuro/ui/components/SearchTextField.kt | 14 ++-- .../com/jetpackduba/gitnuro/ui/diff/Diff.kt | 12 ---- .../com/jetpackduba/gitnuro/ui/log/Log.kt | 5 -- .../viewmodels/CommitChangesViewModel.kt | 33 ++++++++- .../gitnuro/viewmodels/DiffViewModel.kt | 6 +- .../gitnuro/viewmodels/LogViewModel.kt | 20 +++--- .../gitnuro/viewmodels/StatusViewModel.kt | 57 +++++++++++++++- .../gitnuro/viewmodels/TabViewModel.kt | 5 +- .../sidepanel/SidePanelViewModel.kt | 32 ++++++++- 14 files changed, 259 insertions(+), 79 deletions(-) diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/git/TabState.kt b/src/main/kotlin/com/jetpackduba/gitnuro/git/TabState.kt index 876d414..d59fdf3 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/git/TabState.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/git/TabState.kt @@ -352,4 +352,8 @@ enum class RefreshType { enum class CloseableView { DIFF, LOG_SEARCH, + SIDE_PANEL_SEARCH, + COMMIT_CHANGES_SEARCH, + STAGED_CHANGES_SEARCH, + UNSTAGED_CHANGES_SEARCH, } \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/CommitChanges.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/CommitChanges.kt index b235401..c67cb46 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/CommitChanges.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/CommitChanges.kt @@ -81,6 +81,9 @@ fun CommitChanges( onSearchFilterToggled = { visible -> commitChangesViewModel.onSearchFilterToggled(visible) }, + onSearchFocused = { + commitChangesViewModel.addSearchToCloseableView() + }, onSearchFilterChanged = { filter -> searchFilter = filter commitChangesViewModel.onSearchFilterChanged(filter) @@ -105,6 +108,7 @@ private fun CommitChangesView( onHistory: (String) -> Unit, onDiffSelected: (DiffEntry) -> Unit, onSearchFilterToggled: (Boolean) -> Unit, + onSearchFocused: () -> Unit, onSearchFilterChanged: (TextFieldValue) -> Unit, onDirectoryClicked: (TreeItem.Dir) -> Unit, onAlternateShowAsTree: () -> Unit, @@ -129,6 +133,7 @@ private fun CommitChangesView( searchFilter, onSearchFilterChanged, onSearchFilterToggled, + onSearchFocused, showAsTree = showAsTree, onAlternateShowAsTree = onAlternateShowAsTree, ) @@ -182,6 +187,7 @@ private fun Header( searchFilter: TextFieldValue, onSearchFilterChanged: (TextFieldValue) -> Unit, onSearchFilterToggled: (Boolean) -> Unit, + onSearchFocused: () -> Unit, showAsTree: Boolean, onAlternateShowAsTree: () -> Unit, ) { @@ -250,6 +256,7 @@ private fun Header( onSearchFilterChanged = onSearchFilterChanged, searchFocusRequester = searchFocusRequester, onClose = { onSearchFilterToggled(false) }, + onSearchFocused = onSearchFocused, ) } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt index 885601b..a5dcf64 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt @@ -10,8 +10,10 @@ 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.focus.onFocusChanged import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.unit.dp import com.jetpackduba.gitnuro.AppConstants import com.jetpackduba.gitnuro.extensions.handMouseClickable @@ -34,6 +36,9 @@ import kotlinx.coroutines.launch import org.eclipse.jgit.lib.RepositoryState import org.eclipse.jgit.revwalk.RevCommit +val LocalTabFocusRequester = compositionLocalOf { FocusRequester() } + + @Composable fun RepositoryOpenPage( repositoryOpenViewModel: RepositoryOpenViewModel, @@ -109,6 +114,7 @@ fun RepositoryOpenPage( .focusRequester(focusRequester) .focusable(true) .onPreviewKeyEvent { + println("onPreviewKeyEvent: $it") when { it.matchesBinding(KeybindingOption.PULL) -> { repositoryOpenViewModel.pull(PullType.DEFAULT) @@ -154,36 +160,40 @@ fun RepositoryOpenPage( } ) { - Column(modifier = Modifier.weight(1f)) { - Menu( - menuViewModel = repositoryOpenViewModel.tabViewModelsProvider.menuViewModel, - modifier = Modifier - .padding( - vertical = 4.dp - ) - .fillMaxWidth(), - onCreateBranch = { showNewBranchDialog = true }, - onStashWithMessage = { showStashWithMessageDialog = true }, - onOpenAnotherRepository = { repositoryOpenViewModel.openAnotherRepository(it) }, - onOpenAnotherRepositoryFromPicker = { - val repoToOpen = repositoryOpenViewModel.openDirectoryPicker() + CompositionLocalProvider( + LocalTabFocusRequester provides focusRequester + ) { + Column(modifier = Modifier.weight(1f)) { + Menu( + menuViewModel = repositoryOpenViewModel.tabViewModelsProvider.menuViewModel, + modifier = Modifier + .padding( + vertical = 4.dp + ) + .fillMaxWidth(), + onCreateBranch = { showNewBranchDialog = true }, + onStashWithMessage = { showStashWithMessageDialog = true }, + onOpenAnotherRepository = { repositoryOpenViewModel.openAnotherRepository(it) }, + onOpenAnotherRepositoryFromPicker = { + val repoToOpen = repositoryOpenViewModel.openDirectoryPicker() - if (repoToOpen != null) { - repositoryOpenViewModel.openAnotherRepository(repoToOpen) - } - }, - onQuickActions = { showQuickActionsDialog = true }, - onShowSettingsDialog = onShowSettingsDialog - ) + if (repoToOpen != null) { + repositoryOpenViewModel.openAnotherRepository(repoToOpen) + } + }, + onQuickActions = { showQuickActionsDialog = true }, + onShowSettingsDialog = onShowSettingsDialog + ) - RepoContent( - repositoryOpenViewModel = repositoryOpenViewModel, - diffSelected = diffSelected, - selectedItem = selectedItem, - repositoryState = repositoryState, - blameState = blameState, - showHistory = showHistory, - ) + RepoContent( + repositoryOpenViewModel = repositoryOpenViewModel, + diffSelected = diffSelected, + selectedItem = selectedItem, + repositoryState = repositoryState, + blameState = blameState, + showHistory = showHistory, + ) + } } Spacer( @@ -354,10 +364,12 @@ fun MainContentView( val diffViewModel = repositoryOpenViewModel.diffViewModel if (diffViewModel != null) { + val tabFocusRequester = LocalTabFocusRequester.current Diff( diffViewModel = diffViewModel, onCloseDiffView = { repositoryOpenViewModel.newDiffSelected = null + tabFocusRequester.requestFocus() } ) } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/SidePanel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/SidePanel.kt index 59a0325..ca6090d 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/SidePanel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/SidePanel.kt @@ -9,6 +9,9 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -32,6 +35,7 @@ import com.jetpackduba.gitnuro.ui.dialogs.AddSubmodulesDialog import com.jetpackduba.gitnuro.ui.dialogs.SetDefaultUpstreamBranchDialog import com.jetpackduba.gitnuro.viewmodels.ChangeDefaultUpstreamBranchViewModel import com.jetpackduba.gitnuro.viewmodels.sidepanel.* +import kotlinx.coroutines.flow.collectLatest import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.submodule.SubmoduleStatus @@ -47,7 +51,7 @@ fun SidePanel( stashesViewModel: StashesViewModel = sidePanelViewModel.stashesViewModel, submodulesViewModel: SubmodulesViewModel = sidePanelViewModel.submodulesViewModel, ) { - var filter by remember(sidePanelViewModel) { mutableStateOf(sidePanelViewModel.filter.value) } + val filter by sidePanelViewModel.filter.collectAsState() val selectedItem by sidePanelViewModel.selectedItem.collectAsState() val branchesState by branchesViewModel.branchesState.collectAsState() @@ -59,16 +63,31 @@ fun SidePanel( val (showAddEditRemote, setShowAddEditRemote) = remember { mutableStateOf(null) } val (branchToChangeUpstream, setBranchToChangeUpstream) = remember { mutableStateOf(null) } var showEditSubmodulesDialog by remember { mutableStateOf(false) } + val searchFocusRequester = remember { FocusRequester() } + val tabFocusRequester = LocalTabFocusRequester.current + + LaunchedEffect(sidePanelViewModel) { + sidePanelViewModel.freeSearchFocusFlow.collectLatest { + tabFocusRequester.requestFocus() + } + } Column { FilterTextField( value = filter, onValueChange = { newValue -> - filter = newValue sidePanelViewModel.newFilter(newValue) }, modifier = Modifier .padding(start = 8.dp) + .focusRequester(searchFocusRequester) + .onFocusChanged { + if (it.isFocused) { + sidePanelViewModel.addSidePanelSearchToCloseables() + } else { + sidePanelViewModel.removeSidePanelSearchFromCloseables() + } + } ) ScrollableLazyColumn( @@ -142,7 +161,11 @@ fun SidePanel( } @Composable -fun FilterTextField(value: String, onValueChange: (String) -> Unit, modifier: Modifier) { +fun FilterTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier +) { AdjustableOutlinedTextField( value = value, hint = "Search for branches, tags & more", diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt index 82caaa6..4f9dd2c 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt @@ -6,7 +6,10 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.foundation.* +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.focusable +import androidx.compose.foundation.hoverable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsHoveredAsState import androidx.compose.foundation.layout.* @@ -20,6 +23,7 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.compositeOver @@ -49,6 +53,8 @@ import com.jetpackduba.gitnuro.ui.tree_files.TreeItem import com.jetpackduba.gitnuro.viewmodels.CommitterDataRequestState import com.jetpackduba.gitnuro.viewmodels.StageStateUi import com.jetpackduba.gitnuro.viewmodels.StatusViewModel +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch import org.eclipse.jgit.lib.RepositoryState @Composable @@ -88,10 +94,29 @@ fun UncommittedChanges( val canCommit = commitMessage.isNotEmpty() && stageStateUi.hasStagedFiles val canAmend = commitMessage.isNotEmpty() && statusViewModel.hasPreviousCommits + val tabFocusRequester = LocalTabFocusRequester.current LaunchedEffect(statusViewModel) { - statusViewModel.commitMessageChangesFlow.collect { newCommitMessage -> - setCommitMessage(newCommitMessage) + launch { + statusViewModel.commitMessageChangesFlow.collect { newCommitMessage -> + setCommitMessage(newCommitMessage) + } + } + + launch { + statusViewModel.showSearchUnstaged.collectLatest { show -> + if (!show) { + tabFocusRequester.requestFocus() + } + } + } + + launch { + statusViewModel.showSearchStaged.collectLatest { show -> + if (!show) { + tabFocusRequester.requestFocus() + } + } } } @@ -131,6 +156,7 @@ fun UncommittedChanges( stagedListState, selectedEntryType, onSearchFilterToggled = { statusViewModel.onSearchFilterToggledStaged(it) }, + onSearchFocused = { statusViewModel.addStagedSearchToCloseableView() }, onDiffEntryOptionSelected = { statusViewModel.unstage(it) }, onDiffEntrySelected = onStagedDiffEntrySelected, onSearchFilterChanged = { statusViewModel.onSearchFilterChangedStaged(it) }, @@ -154,6 +180,7 @@ fun UncommittedChanges( unstagedListState, selectedEntryType, onSearchFilterToggled = { statusViewModel.onSearchFilterToggledUnstaged(it) }, + onSearchFocused = { statusViewModel.addUnstagedSearchToCloseableView() }, onDiffEntryOptionSelected = { statusViewModel.stage(it) }, onDiffEntrySelected = onUnstagedDiffEntrySelected, onSearchFilterChanged = { statusViewModel.onSearchFilterChangedUnstaged(it) }, @@ -323,6 +350,7 @@ fun ColumnScope.StagedView( stagedListState: LazyListState, selectedEntryType: DiffType?, onSearchFilterToggled: (Boolean) -> Unit, + onSearchFocused: () -> Unit, onDiffEntryOptionSelected: (StatusEntry) -> Unit, onDiffEntrySelected: (StatusEntry) -> Unit, onSearchFilterChanged: (TextFieldValue) -> Unit, @@ -356,6 +384,7 @@ fun ColumnScope.StagedView( listState = stagedListState, selectedEntryType = selectedEntryType, onSearchFilterToggled = onSearchFilterToggled, + onSearchFocused = onSearchFocused, onDiffEntryOptionSelected = onDiffEntryOptionSelected, onDiffEntrySelected = onDiffEntrySelected, onSearchFilterChanged = onSearchFilterChanged, @@ -381,6 +410,7 @@ fun ColumnScope.UnstagedView( unstagedListState: LazyListState, selectedEntryType: DiffType?, onSearchFilterToggled: (Boolean) -> Unit, + onSearchFocused: () -> Unit, onDiffEntryOptionSelected: (StatusEntry) -> Unit, onDiffEntrySelected: (StatusEntry) -> Unit, onSearchFilterChanged: (TextFieldValue) -> Unit, @@ -414,6 +444,7 @@ fun ColumnScope.UnstagedView( listState = unstagedListState, selectedEntryType = selectedEntryType, onSearchFilterToggled = onSearchFilterToggled, + onSearchFocused = onSearchFocused, onDiffEntryOptionSelected = onDiffEntryOptionSelected, onDiffEntrySelected = onDiffEntrySelected, onSearchFilterChanged = onSearchFilterChanged, @@ -448,6 +479,7 @@ fun ColumnScope.NeutralView( onTreeEntries: (StageStateUi.TreeLoaded) -> List>, onListEntries: (StageStateUi.ListLoaded) -> List, onSearchFilterToggled: (Boolean) -> Unit, + onSearchFocused: () -> Unit, onDiffEntryOptionSelected: (StatusEntry) -> Unit, onDiffEntrySelected: (StatusEntry) -> Unit, onSearchFilterChanged: (TextFieldValue) -> Unit, @@ -477,6 +509,7 @@ fun ColumnScope.NeutralView( showSearch = showSearchUnstaged, searchFilter = searchFilterUnstaged, onSearchFilterToggled = onSearchFilterToggled, + onSearchFocused = onSearchFocused, onSearchFilterChanged = onSearchFilterChanged, statusEntries = onTreeEntries(stageStateUi), lazyListState = listState, @@ -516,6 +549,7 @@ fun ColumnScope.NeutralView( showSearch = showSearchUnstaged, searchFilter = searchFilterUnstaged, onSearchFilterToggled = onSearchFilterToggled, + onSearchFocused = onSearchFocused, onSearchFilterChanged = onSearchFilterChanged, statusEntries = onListEntries(stageStateUi), lazyListState = listState, @@ -789,6 +823,7 @@ private fun EntriesList( showSearch: Boolean, searchFilter: TextFieldValue, onSearchFilterToggled: (Boolean) -> Unit, + onSearchFocused: () -> Unit, onSearchFilterChanged: (TextFieldValue) -> Unit, statusEntries: List, lazyListState: LazyListState, @@ -816,6 +851,7 @@ private fun EntriesList( onSearchFilterToggled = onSearchFilterToggled, showAsTree = false, showSearch = showSearch, + onSearchFocused = onSearchFocused, ) @@ -859,6 +895,7 @@ private fun TreeEntriesList( showSearch: Boolean, searchFilter: TextFieldValue, onSearchFilterToggled: (Boolean) -> Unit, + onSearchFocused: () -> Unit, onSearchFilterChanged: (TextFieldValue) -> Unit, statusEntries: List>, lazyListState: LazyListState, @@ -886,6 +923,7 @@ private fun TreeEntriesList( searchFilter = searchFilter, onSearchFilterChanged = onSearchFilterChanged, onSearchFilterToggled = onSearchFilterToggled, + onSearchFocused = onSearchFocused, showAsTree = true, showSearch = showSearch, ) @@ -939,6 +977,7 @@ fun EntriesHeader( onAllAction: () -> Unit, onAlternateShowAsTree: () -> Unit, onSearchFilterToggled: (Boolean) -> Unit, + onSearchFocused: () -> Unit, searchFilter: TextFieldValue, onSearchFilterChanged: (TextFieldValue) -> Unit, ) { @@ -1022,6 +1061,7 @@ fun EntriesHeader( searchFilter = searchFilter, onSearchFilterChanged = onSearchFilterChanged, searchFocusRequester = searchFocusRequester, + onSearchFocused = onSearchFocused, onClose = { onSearchFilterToggled(false) }, ) } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/SearchTextField.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/SearchTextField.kt index fd5fb8b..0d44ab8 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/SearchTextField.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/components/SearchTextField.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.type @@ -28,20 +29,14 @@ import com.jetpackduba.gitnuro.keybindings.matchesBinding fun SearchTextField( searchFilter: TextFieldValue, onSearchFilterChanged: (TextFieldValue) -> Unit, + onSearchFocused: () -> Unit, searchFocusRequester: FocusRequester, onClose: () -> Unit, ) { Box( modifier = Modifier .background(MaterialTheme.colors.background) - .padding(horizontal = 4.dp, vertical = 4.dp) - .onPreviewKeyEvent { keyEvent -> - if (keyEvent.matchesBinding(KeybindingOption.EXIT) && keyEvent.type == KeyEventType.KeyDown) { - onClose() - true - } else - false - }, + .padding(horizontal = 4.dp, vertical = 4.dp), ) { AdjustableOutlinedTextField( value = searchFilter, @@ -51,7 +46,8 @@ fun SearchTextField( hint = "Search files by name or path", modifier = Modifier.fillMaxWidth() .focusable() - .focusRequester(searchFocusRequester), + .focusRequester(searchFocusRequester) + .onFocusChanged { if (it.isFocused) onSearchFocused() }, trailingIcon = { IconButton( onClick = onClose, diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/diff/Diff.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/diff/Diff.kt index 38c25b5..1f2c1f2 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/diff/Diff.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/diff/Diff.kt @@ -24,9 +24,6 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.input.key.KeyEventType -import androidx.compose.ui.input.key.onPreviewKeyEvent -import androidx.compose.ui.input.key.type import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.res.loadImageBitmap @@ -49,8 +46,6 @@ import com.jetpackduba.gitnuro.git.diff.Line import com.jetpackduba.gitnuro.git.diff.LineType import com.jetpackduba.gitnuro.git.workspace.StatusEntry import com.jetpackduba.gitnuro.git.workspace.StatusType -import com.jetpackduba.gitnuro.keybindings.KeybindingOption -import com.jetpackduba.gitnuro.keybindings.matchesBinding import com.jetpackduba.gitnuro.theme.* import com.jetpackduba.gitnuro.ui.components.PrimaryButton import com.jetpackduba.gitnuro.ui.components.ScrollableLazyColumn @@ -113,13 +108,6 @@ fun Diff( diffViewModel.addToCloseables() } } - .onPreviewKeyEvent { keyEvent -> - if (keyEvent.matchesBinding(KeybindingOption.EXIT) && keyEvent.type == KeyEventType.KeyDown) { - onCloseDiffView() - true - } else - false - } ) { when (viewDiffResult) { ViewDiffResult.DiffNotFound -> { diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/log/Log.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/log/Log.kt index c24c323..6829af6 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/log/Log.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/log/Log.kt @@ -403,11 +403,6 @@ fun SearchFilter( true } - keyEvent.matchesBinding(KeybindingOption.EXIT) -> { - logViewModel.closeSearch() - true - } - else -> false } }, diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/CommitChangesViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/CommitChangesViewModel.kt index d94e251..2841585 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/CommitChangesViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/CommitChangesViewModel.kt @@ -7,6 +7,7 @@ import com.jetpackduba.gitnuro.extensions.delayedStateChange import com.jetpackduba.gitnuro.extensions.filePath import com.jetpackduba.gitnuro.extensions.fullData import com.jetpackduba.gitnuro.extensions.lowercaseContains +import com.jetpackduba.gitnuro.git.CloseableView import com.jetpackduba.gitnuro.git.RefreshType import com.jetpackduba.gitnuro.git.TabState import com.jetpackduba.gitnuro.git.diff.GetCommitDiffEntriesUseCase @@ -15,6 +16,7 @@ import com.jetpackduba.gitnuro.ui.tree_files.TreeItem import com.jetpackduba.gitnuro.ui.tree_files.entriesToTreeEntry import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.revwalk.RevCommit import javax.inject.Inject @@ -25,7 +27,7 @@ class CommitChangesViewModel @Inject constructor( private val tabState: TabState, private val getCommitDiffEntriesUseCase: GetCommitDiffEntriesUseCase, private val appSettingsRepository: AppSettingsRepository, - tabScope: CoroutineScope, + private val tabScope: CoroutineScope, ) { private val _showSearch = MutableStateFlow(false) val showSearch: StateFlow = _showSearch @@ -82,6 +84,26 @@ class CommitChangesViewModel @Inject constructor( ) + init { + tabScope.launch { + _showSearch.collectLatest { + if (it) { + addSearchToCloseableView() + } else { + removeSearchFromCloseableView() + } + } + } + + tabScope.launch { + tabState.closeViewFlow.collectLatest { + if (it == CloseableView.COMMIT_CHANGES_SEARCH) { + onSearchFilterToggled(false) + } + } + } + } + fun loadChanges(commit: RevCommit) = tabState.runOperation( refreshType = RefreshType.NONE, ) { git -> @@ -153,6 +175,14 @@ class CommitChangesViewModel @Inject constructor( fun onSearchFilterChanged(filter: TextFieldValue) { _searchFilter.value = filter } + + fun addSearchToCloseableView() = tabScope.launch { + tabState.addCloseableView(CloseableView.COMMIT_CHANGES_SEARCH) + } + + private fun removeSearchFromCloseableView() = tabScope.launch { + tabState.removeCloseableView(CloseableView.COMMIT_CHANGES_SEARCH) + } } private sealed interface CommitChangesState { @@ -167,6 +197,7 @@ sealed interface CommitChangesStateUi { sealed interface Loaded : CommitChangesStateUi { val commit: RevCommit } + data class ListLoaded(override val commit: RevCommit, val changes: List) : Loaded diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/DiffViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/DiffViewModel.kt index 4c39422..14f5fcf 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/DiffViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/DiffViewModel.kt @@ -38,7 +38,7 @@ class DiffViewModel @Inject constructor( private val generateSplitHunkFromDiffResultUseCase: GenerateSplitHunkFromDiffResultUseCase, private val discardUnstagedHunkLineUseCase: DiscardUnstagedHunkLineUseCase, private val tabsManager: TabsManager, - tabScope: CoroutineScope, + private val tabScope: CoroutineScope, ) : AutoCloseable { private val _diffResult = MutableStateFlow(ViewDiffResult.Loading("")) val diffResult: StateFlow = _diffResult @@ -229,11 +229,11 @@ class DiffViewModel @Inject constructor( tabsManager.addNewTabFromPath("${git.repository.workTree}/$path", true) } - fun addToCloseables() = tabState.runOperation(refreshType = RefreshType.NONE) { _ -> + fun addToCloseables() = tabScope.launch { tabState.addCloseableView(CloseableView.DIFF) } - private fun removeFromCloseables() = tabState.runOperation(refreshType = RefreshType.NONE) { _ -> + private fun removeFromCloseables() = tabScope.launch { tabState.removeCloseableView(CloseableView.DIFF) } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/LogViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/LogViewModel.kt index 592e55f..259432c 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/LogViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/LogViewModel.kt @@ -59,7 +59,7 @@ class LogViewModel @Inject constructor( private val startRebaseInteractiveUseCase: StartRebaseInteractiveUseCase, private val tabState: TabState, private val appSettingsRepository: AppSettingsRepository, - tabScope: CoroutineScope, + private val tabScope: CoroutineScope, sharedStashViewModel: SharedStashViewModel, sharedBranchesViewModel: SharedBranchesViewModel, sharedRemotesViewModel: SharedRemotesViewModel, @@ -133,6 +133,15 @@ class LogViewModel @Inject constructor( } } } + + tabScope.launch { + _logSearchFilterResults.collectLatest { + when (it) { + LogSearch.NotSearching -> removeSearchFromCloseableView() + is LogSearch.SearchResults -> addSearchToCloseableView() + } + } + } } @@ -334,10 +343,8 @@ class LogViewModel @Inject constructor( } _logSearchFilterResults.value = LogSearch.SearchResults(matchingCommits, startingUiIndex) - addSearchToCloseableView() } else { _logSearchFilterResults.value = LogSearch.SearchResults(emptyList(), NONE_MATCHING_INDEX) - addSearchToCloseableView() } } @@ -359,7 +366,6 @@ class LogViewModel @Inject constructor( _logSearchFilterResults.value = logSearchFilterResultsValue.copy(index = newIndex) _focusCommit.emit(newCommitToSelect) - addSearchToCloseableView() } suspend fun selectNextFilterCommit() { @@ -382,7 +388,6 @@ class LogViewModel @Inject constructor( _logSearchFilterResults.value = logSearchFilterResultsValue.copy(index = newIndex) _focusCommit.emit(newCommitToSelect) - addSearchToCloseableView() } fun showDialog(dialog: LogDialog) { @@ -391,14 +396,13 @@ class LogViewModel @Inject constructor( fun closeSearch() { _logSearchFilterResults.value = LogSearch.NotSearching - removeSearchFromCloseableView() } - fun addSearchToCloseableView() = tabState.runOperation(refreshType = RefreshType.NONE) { _ -> + fun addSearchToCloseableView() = tabScope.launch { tabState.addCloseableView(CloseableView.LOG_SEARCH) } - private fun removeSearchFromCloseableView() = tabState.runOperation(refreshType = RefreshType.NONE) { _ -> + private fun removeSearchFromCloseableView() = tabScope.launch { tabState.removeCloseableView(CloseableView.LOG_SEARCH) } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt index 191f2a6..a538db5 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/StatusViewModel.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.text.input.TextFieldValue import com.jetpackduba.gitnuro.SharedRepositoryStateManager import com.jetpackduba.gitnuro.TaskType import com.jetpackduba.gitnuro.extensions.* +import com.jetpackduba.gitnuro.git.CloseableView import com.jetpackduba.gitnuro.git.RefreshType import com.jetpackduba.gitnuro.git.TabState import com.jetpackduba.gitnuro.git.author.LoadAuthorUseCase @@ -59,7 +60,7 @@ class StatusViewModel @Inject constructor( private val sharedRepositoryStateManager: SharedRepositoryStateManager, private val getSpecificCommitMessageUseCase: GetSpecificCommitMessageUseCase, private val appSettingsRepository: AppSettingsRepository, - tabScope: CoroutineScope, + private val tabScope: CoroutineScope, ) { private val _showSearchUnstaged = MutableStateFlow(false) val showSearchUnstaged: StateFlow = _showSearchUnstaged @@ -182,6 +183,36 @@ class StatusViewModel @Inject constructor( refresh(tabState.git) } } + + tabScope.launch { + showSearchStaged.collectLatest { + if (it) { + addStagedSearchToCloseableView() + } else { + removeStagedSearchToCloseableView() + } + } + } + + tabScope.launch { + showSearchUnstaged.collectLatest { + if (it) { + addUnstagedSearchToCloseableView() + } else { + removeUnstagedSearchToCloseableView() + } + } + } + + tabScope.launch { + tabState.closeViewFlow.collectLatest { + if (it == CloseableView.STAGED_CHANGES_SEARCH) { + onSearchFilterToggledStaged(false) + } else if (it == CloseableView.UNSTAGED_CHANGES_SEARCH) { + onSearchFilterToggledUnstaged(false) + } + } + } } private fun persistMessage() = tabState.runOperation( @@ -547,6 +578,30 @@ class StatusViewModel @Inject constructor( ) { git -> unstageByDirectoryUseCase(git, dir) } + + fun addStagedSearchToCloseableView() { + addSearchToCloseView(CloseableView.STAGED_CHANGES_SEARCH) + } + + private fun removeStagedSearchToCloseableView() { + removeSearchFromCloseView(CloseableView.STAGED_CHANGES_SEARCH) + } + + fun addUnstagedSearchToCloseableView() { + addSearchToCloseView(CloseableView.UNSTAGED_CHANGES_SEARCH) + } + + private fun removeUnstagedSearchToCloseableView() { + removeSearchFromCloseView(CloseableView.UNSTAGED_CHANGES_SEARCH) + } + + private fun addSearchToCloseView(view: CloseableView) = tabScope.launch { + tabState.addCloseableView(view) + } + + private fun removeSearchFromCloseView(view: CloseableView) = tabScope.launch { + tabState.removeCloseableView(view) + } } sealed interface StageState { diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModel.kt index 0ee3fc7..f91e11e 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/TabViewModel.kt @@ -75,9 +75,8 @@ class TabViewModel @Inject constructor( val errorsManager: ErrorsManager = tabState.errorsManager val selectedItem: StateFlow = tabState.selectedItem - val repositoryOpenViewModel: RepositoryOpenViewModel by lazy { - repositoryOpenViewModelProvider.get() - } + val repositoryOpenViewModel: RepositoryOpenViewModel = repositoryOpenViewModelProvider.get() + private val _repositorySelectionStatus = MutableStateFlow(RepositorySelectionStatus.None) val repositorySelectionStatus: StateFlow get() = _repositorySelectionStatus diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/sidepanel/SidePanelViewModel.kt b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/sidepanel/SidePanelViewModel.kt index 79849d6..ab6451d 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/sidepanel/SidePanelViewModel.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/viewmodels/sidepanel/SidePanelViewModel.kt @@ -1,10 +1,13 @@ package com.jetpackduba.gitnuro.viewmodels.sidepanel +import androidx.compose.runtime.collectAsState import com.jetpackduba.gitnuro.di.factories.* +import com.jetpackduba.gitnuro.git.CloseableView import com.jetpackduba.gitnuro.git.TabState import com.jetpackduba.gitnuro.ui.SelectedItem -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import javax.inject.Inject class SidePanelViewModel @Inject constructor( @@ -13,7 +16,8 @@ class SidePanelViewModel @Inject constructor( tagsViewModelFactory: TagsViewModelFactory, stashesViewModelFactory: StashesViewModelFactory, submodulesViewModelFactory: SubmodulesViewModelFactory, - tabState: TabState, + private val tabState: TabState, + private val tabScope: CoroutineScope, ) { private val _filter = MutableStateFlow("") val filter: StateFlow = _filter @@ -25,7 +29,29 @@ class SidePanelViewModel @Inject constructor( val stashesViewModel: StashesViewModel = stashesViewModelFactory.create(filter) val submodulesViewModel: SubmodulesViewModel = submodulesViewModelFactory.create(filter) + private val _freeSearchFocusFlow = MutableSharedFlow() + val freeSearchFocusFlow = _freeSearchFocusFlow.asSharedFlow() + + init { + tabScope.launch { + tabState.closeViewFlow.collectLatest { + if (it == CloseableView.SIDE_PANEL_SEARCH) { + newFilter("") + _freeSearchFocusFlow.emit(Unit) + } + } + } + } + fun newFilter(newValue: String) { _filter.value = newValue } + + fun addSidePanelSearchToCloseables() = tabScope.launch { + tabState.addCloseableView(CloseableView.SIDE_PANEL_SEARCH) + } + + fun removeSidePanelSearchFromCloseables() = tabScope.launch { + tabState.removeCloseableView(CloseableView.SIDE_PANEL_SEARCH) + } }