diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/keybindings/Keybinding.kt b/src/main/kotlin/com/jetpackduba/gitnuro/keybindings/Keybinding.kt index 124cd02..bac7506 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/keybindings/Keybinding.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/keybindings/Keybinding.kt @@ -26,15 +26,26 @@ enum class KeybindingOption { /** * Used to accept multi-line text field like the commit message */ - ACCEPT, + TEXT_ACCEPT, /** * Used to close dialogs or components */ EXIT, + + /** + * Used to go up in lists + */ + UP, + + /** + * Used to go down in lists + */ + DOWN, } +@OptIn(ExperimentalComposeUiApi::class) private fun baseKeybindings() = mapOf( KeybindingOption.REFRESH to listOf( Keybinding(key = Key.F5), @@ -43,12 +54,18 @@ private fun baseKeybindings() = mapOf( KeybindingOption.SIMPLE_ACCEPT to listOf( Keybinding(key = Key.Enter), ), - KeybindingOption.ACCEPT to listOf( + KeybindingOption.TEXT_ACCEPT to listOf( Keybinding(control = true, key = Key.Enter), ), KeybindingOption.EXIT to listOf( Keybinding(key = Key.Escape), ), + KeybindingOption.UP to listOf( + Keybinding(key = Key.DirectionUp), + ), + KeybindingOption.DOWN to listOf( + Keybinding(key = Key.DirectionDown), + ), ) private fun linuxKeybindings(): Map> = baseKeybindings() diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/Menu.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/Menu.kt index 9a6e216..9d79e3a 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/Menu.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/Menu.kt @@ -35,9 +35,8 @@ fun Menu( onCreateBranch: () -> Unit, onGoToWorkspace: () -> Unit, onStashWithMessage: () -> Unit, + onQuickActions: () -> Unit, ) { - var showAdditionalOptionsDropDownMenu by remember { mutableStateOf(false) } - Row( modifier = modifier, horizontalArrangement = Arrangement.Center, @@ -113,34 +112,13 @@ fun Menu( Spacer(modifier = Modifier.weight(1f)) - Box { - IconMenuButton( - modifier = Modifier - .padding(end = 8.dp) - .size(36.dp), - icon = painterResource("more_vert.svg"), - onClick = { - showAdditionalOptionsDropDownMenu = true - }, - ) - DropdownMenu( - expanded = showAdditionalOptionsDropDownMenu, - content = { - val menuOptions = remember { - repositoryAdditionalOptionsMenu( - onOpenRepositoryOnFileExplorer = { menuViewModel.openFolderInFileExplorer() }, - ) - } - for (item in menuOptions) { - DropDownContent( - dropDownContentData = item, - onDismiss = { showAdditionalOptionsDropDownMenu = false } - ) - } - }, - onDismissRequest = { showAdditionalOptionsDropDownMenu = false } - ) - } + MenuButton( + title = "Quick actions", + modifier = Modifier.padding(end = 16.dp), + icon = painterResource("bolt.svg"), + fixedWidth = false, + onClick = onQuickActions, + ) } } diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt index 5eff111..a64dfa6 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/RepositoryOpen.kt @@ -39,6 +39,7 @@ import com.jetpackduba.gitnuro.theme.secondarySurface import com.jetpackduba.gitnuro.ui.components.ScrollableColumn import com.jetpackduba.gitnuro.ui.dialogs.AuthorDialog import com.jetpackduba.gitnuro.ui.dialogs.NewBranchDialog +import com.jetpackduba.gitnuro.ui.dialogs.QuickActionsDialog import com.jetpackduba.gitnuro.ui.dialogs.StashWithMessageDialog import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog import com.jetpackduba.gitnuro.ui.diff.Diff @@ -64,6 +65,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) { var showNewBranchDialog by remember { mutableStateOf(false) } var showStashWithMessageDialog by remember { mutableStateOf(false) } + var showQuickActionsDialog by remember { mutableStateOf(false) } if (showNewBranchDialog) { NewBranchDialog( @@ -95,6 +97,13 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) { } ) } + } else if (showQuickActionsDialog) { + QuickActionsDialog( + onClose = { showQuickActionsDialog = false }, + onAction = { + showQuickActionsDialog = false + }, + ) } val focusRequester = remember { FocusRequester() } @@ -135,7 +144,8 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) { menuViewModel = tabViewModel.menuViewModel, onCreateBranch = { showNewBranchDialog = true }, onStashWithMessage = { showStashWithMessageDialog = true }, - onGoToWorkspace = { tabViewModel.selectUncommitedChanges() } + onGoToWorkspace = { tabViewModel.selectUncommitedChanges() }, + onQuickActions = { showQuickActionsDialog = true } ) RepoContent(tabViewModel, diffSelected, selectedItem, repositoryState, blameState, showHistory) diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt index b751f7f..7f57d35 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/UncommitedChanges.kt @@ -172,7 +172,7 @@ fun UncommitedChanges( .fillMaxWidth() .weight(weight = 1f, fill = true) .onPreviewKeyEvent { keyEvent -> - if (keyEvent.matchesBinding(KeybindingOption.ACCEPT) && canCommit) { + if (keyEvent.matchesBinding(KeybindingOption.TEXT_ACCEPT) && canCommit) { doCommit(false) true } else diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/QuickActionsDialog.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/QuickActionsDialog.kt new file mode 100644 index 0000000..0bfabfd --- /dev/null +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/dialogs/QuickActionsDialog.kt @@ -0,0 +1,126 @@ +package com.jetpackduba.gitnuro.ui.dialogs + +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.Icon +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.draw.clip +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.input.key.onKeyEvent +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.jetpackduba.gitnuro.extensions.backgroundIf +import com.jetpackduba.gitnuro.extensions.handMouseClickable +import com.jetpackduba.gitnuro.keybindings.KeybindingOption +import com.jetpackduba.gitnuro.keybindings.matchesBinding +import com.jetpackduba.gitnuro.theme.backgroundSelected +import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField + +@Composable +fun QuickActionsDialog( + onClose: () -> Unit, + onAction: (QuickActionType) -> Unit, +) { + + val textFieldFocusRequester = remember { FocusRequester() } + val items = remember { + listOf( + QuickAction("open.svg", "Open project in file manager", QuickActionType.OPEN_DIR_IN_FILE_MANAGER), + QuickAction("download.svg", "Clone new repository", QuickActionType.CLONE), + ) + } + + var searchFilter by remember { mutableStateOf("") } + + val filteredItems by remember(searchFilter) { + derivedStateOf { items.filter { it.title.contains(searchFilter, ignoreCase = true) } } + } + + var selectedIndex by remember(filteredItems) { + mutableStateOf(0) + } + + LaunchedEffect(Unit) { + textFieldFocusRequester.requestFocus() + } + + MaterialDialog( + onCloseRequested = onClose, + background = MaterialTheme.colors.surface, + ) { + Column( + modifier = Modifier.width(680.dp) + .height(400.dp) + .onKeyEvent { keyEvent -> + if (keyEvent.matchesBinding(KeybindingOption.DOWN)) { + if (selectedIndex < filteredItems.count() - 1) + selectedIndex++ + true + } else if (keyEvent.matchesBinding(KeybindingOption.UP)) { + if (selectedIndex > 0) + selectedIndex-- + true + } else if (keyEvent.matchesBinding(KeybindingOption.SIMPLE_ACCEPT)) { + val item = filteredItems.getOrNull(selectedIndex) + if (item != null) + onAction(item.type) + true + } else + false + } + ) { + AdjustableOutlinedTextField( + value = searchFilter, + onValueChange = { searchFilter = it }, + maxLines = 1, + modifier = Modifier.fillMaxWidth() + .padding(bottom = 16.dp) + .focusRequester(textFieldFocusRequester) + ) + + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + itemsIndexed(filteredItems) { index, item -> + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(4.dp)) + .backgroundIf(selectedIndex == index, MaterialTheme.colors.backgroundSelected) + .handMouseClickable { onAction(item.type) } + ) { + Icon( + painterResource(item.icon), + contentDescription = null, + modifier = Modifier.padding(vertical = 16.dp, horizontal = 16.dp), + tint = MaterialTheme.colors.onBackground, + ) + + Text( + item.title, + color = MaterialTheme.colors.onBackground, + style = MaterialTheme.typography.body1, + ) + } + } + } + } + } +} + +data class QuickAction(val icon: String, val title: String, val type: QuickActionType) + +enum class QuickActionType { + OPEN_DIR_IN_FILE_MANAGER, + CLONE; +} \ No newline at end of file