Replaced side bar

This commit is contained in:
Abdelilah El Aissaoui 2022-10-23 01:41:04 +02:00
parent 47d1e89af2
commit 27b9416598
5 changed files with 126 additions and 160 deletions

View File

@ -22,8 +22,10 @@ import androidx.compose.ui.unit.sp
import com.jetpackduba.gitnuro.LoadingRepository import com.jetpackduba.gitnuro.LoadingRepository
import com.jetpackduba.gitnuro.LocalTabScope import com.jetpackduba.gitnuro.LocalTabScope
import com.jetpackduba.gitnuro.credentials.CredentialsState import com.jetpackduba.gitnuro.credentials.CredentialsState
import com.jetpackduba.gitnuro.ui.dialogs.CloneDialog
import com.jetpackduba.gitnuro.ui.dialogs.PasswordDialog import com.jetpackduba.gitnuro.ui.dialogs.PasswordDialog
import com.jetpackduba.gitnuro.ui.dialogs.UserPasswordDialog import com.jetpackduba.gitnuro.ui.dialogs.UserPasswordDialog
import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog
import com.jetpackduba.gitnuro.viewmodels.RepositorySelectionStatus import com.jetpackduba.gitnuro.viewmodels.RepositorySelectionStatus
import com.jetpackduba.gitnuro.viewmodels.TabViewModel import com.jetpackduba.gitnuro.viewmodels.TabViewModel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -58,11 +60,34 @@ fun AppTab(
CredentialsDialog(tabViewModel) CredentialsDialog(tabViewModel)
var showSettingsDialog by remember { mutableStateOf(false) }
if (showSettingsDialog) {
SettingsDialog(
onDismiss = { showSettingsDialog = false }
)
}
var showCloneDialog by remember { mutableStateOf(false) }
if (showCloneDialog) {
CloneDialog(
onClose = {
showCloneDialog = false
},
onOpenRepository = { dir ->
tabViewModel.openRepository(dir)
},
)
}
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
Crossfade(targetState = repositorySelectionStatus) { Crossfade(targetState = repositorySelectionStatus) {
when (repositorySelectionStatusValue) { when (repositorySelectionStatusValue) {
RepositorySelectionStatus.None -> { RepositorySelectionStatus.None -> {
WelcomePage(tabViewModel = tabViewModel) WelcomePage(
tabViewModel = tabViewModel,
onShowCloneDialog = { showSettingsDialog = true }
)
} }
is RepositorySelectionStatus.Opening -> { is RepositorySelectionStatus.Opening -> {
@ -70,7 +95,11 @@ fun AppTab(
} }
is RepositorySelectionStatus.Open -> { is RepositorySelectionStatus.Open -> {
RepositoryOpenPage(tabViewModel = tabViewModel) RepositoryOpenPage(
tabViewModel = tabViewModel,
onShowSettingsDialog = { showSettingsDialog = true },
onShowCloneDialog = { showCloneDialog = true },
)
} }
} }
} }

View File

@ -2,43 +2,35 @@
package com.jetpackduba.gitnuro.ui package com.jetpackduba.gitnuro.ui
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.focusable 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.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.onKeyEvent import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.pointer.PointerIcon import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import com.jetpackduba.gitnuro.extensions.handMouseClickable import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.git.DiffEntryType import com.jetpackduba.gitnuro.git.DiffEntryType
import com.jetpackduba.gitnuro.keybindings.KeybindingOption import com.jetpackduba.gitnuro.keybindings.KeybindingOption
import com.jetpackduba.gitnuro.keybindings.matchesBinding import com.jetpackduba.gitnuro.keybindings.matchesBinding
import com.jetpackduba.gitnuro.theme.secondarySurface import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
import com.jetpackduba.gitnuro.ui.components.ScrollableColumn import com.jetpackduba.gitnuro.ui.components.ScrollableColumn
import com.jetpackduba.gitnuro.ui.dialogs.* import com.jetpackduba.gitnuro.ui.dialogs.*
import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog
import com.jetpackduba.gitnuro.ui.diff.Diff import com.jetpackduba.gitnuro.ui.diff.Diff
import com.jetpackduba.gitnuro.ui.log.Log import com.jetpackduba.gitnuro.ui.log.Log
import com.jetpackduba.gitnuro.viewmodels.BlameState import com.jetpackduba.gitnuro.viewmodels.BlameState
@ -52,7 +44,11 @@ import org.jetbrains.compose.splitpane.rememberSplitPaneState
import java.awt.Cursor import java.awt.Cursor
@Composable @Composable
fun RepositoryOpenPage(tabViewModel: TabViewModel) { fun RepositoryOpenPage(
tabViewModel: TabViewModel,
onShowSettingsDialog: () -> Unit,
onShowCloneDialog: () -> Unit,
) {
val repositoryState by tabViewModel.repositoryState.collectAsState() val repositoryState by tabViewModel.repositoryState.collectAsState()
val diffSelected by tabViewModel.diffSelected.collectAsState() val diffSelected by tabViewModel.diffSelected.collectAsState()
val selectedItem by tabViewModel.selectedItem.collectAsState() val selectedItem by tabViewModel.selectedItem.collectAsState()
@ -101,7 +97,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
showQuickActionsDialog = false showQuickActionsDialog = false
when (it) { when (it) {
QuickActionType.OPEN_DIR_IN_FILE_MANAGER -> tabViewModel.openFolderInFileExplorer() QuickActionType.OPEN_DIR_IN_FILE_MANAGER -> tabViewModel.openFolderInFileExplorer()
QuickActionType.CLONE -> TODO() QuickActionType.CLONE -> onShowCloneDialog()
} }
}, },
) )
@ -114,8 +110,6 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
} }
Column { Column {
Row(modifier = Modifier.weight(1f)) { Row(modifier = Modifier.weight(1f)) {
SideBar(tabViewModel)
Column( Column(
modifier = Modifier modifier = Modifier
.focusRequester(focusRequester) .focusRequester(focusRequester)
@ -148,7 +142,15 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
onQuickActions = { showQuickActionsDialog = true } onQuickActions = { showQuickActionsDialog = true }
) )
RepoContent(tabViewModel, diffSelected, selectedItem, repositoryState, blameState, showHistory) RepoContent(
tabViewModel = tabViewModel,
diffSelected = diffSelected,
selectedItem = selectedItem,
repositoryState = repositoryState,
blameState = blameState,
showHistory = showHistory,
onShowSettingsDialog = onShowSettingsDialog
)
} }
} }
} }
@ -165,51 +167,6 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
} }
} }
@Composable
fun SideBar(tabViewModel: TabViewModel) {
var showSettingsDialog by remember { mutableStateOf(false) }
if (showSettingsDialog) {
SettingsDialog(
onDismiss = { showSettingsDialog = false }
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.width(48.dp)
.background(MaterialTheme.colors.secondarySurface)
.padding(vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SideBarButton(
painterName = "open.svg",
label = "Open a new repository",
onClick = {
openRepositoryDialog(tabViewModel = tabViewModel)
}
)
Spacer(modifier = Modifier.weight(1f))
SideBarButton(
modifier = Modifier.padding(bottom = 16.dp),
painterName = "refresh.svg",
label = "Refresh repository information",
onClick = {
tabViewModel.refreshAll()
}
)
SideBarButton(
painterName = "settings.svg",
label = "Settings",
onClick = { showSettingsDialog = true }
)
}
}
@Composable @Composable
private fun BottomInfoBar(tabViewModel: TabViewModel) { private fun BottomInfoBar(tabViewModel: TabViewModel) {
val userInfo by tabViewModel.authorInfoSimple.collectAsState() val userInfo by tabViewModel.authorInfoSimple.collectAsState()
@ -237,70 +194,6 @@ private fun BottomInfoBar(tabViewModel: TabViewModel) {
} }
} }
@Composable
fun SideBarButton(
modifier: Modifier = Modifier,
painterName: String,
label: String,
onClick: () -> Unit
) {
val hoverInteraction = remember { MutableInteractionSource() }
val isHovered by hoverInteraction.collectIsHoveredAsState()
val (buttonCoordinates, setButtonCoordinates) = remember { mutableStateOf<Pair<Offset, IntSize>?>(null) }
if (isHovered && buttonCoordinates != null) {
Popup(
popupPositionProvider = object : PopupPositionProvider {
override fun calculatePosition(
anchorBounds: IntRect,
windowSize: IntSize,
layoutDirection: LayoutDirection,
popupContentSize: IntSize
): IntOffset {
val position = buttonCoordinates.first
val size = buttonCoordinates.second
val x = position.x + size.width + 8
val y = position.y + (size.height / 2) - (popupContentSize.height / 2)
return IntOffset(x.toInt(), y.toInt())
}
}
) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(4.dp))
.background(MaterialTheme.colors.background)
) {
Text(
text = label,
color = MaterialTheme.colors.onBackground,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
)
}
}
}
IconButton(
onClick = onClick,
modifier = modifier
.handOnHover()
.hoverable(hoverInteraction)
.size(24.dp)
.onGloballyPositioned { layoutCoordinates ->
setButtonCoordinates(layoutCoordinates.positionInRoot() to layoutCoordinates.size)
}
) {
Icon(
painter = painterResource(painterName),
contentDescription = null,
modifier = Modifier,
tint = MaterialTheme.colors.onBackground,
)
}
}
@Composable @Composable
fun RepoContent( fun RepoContent(
tabViewModel: TabViewModel, tabViewModel: TabViewModel,
@ -309,6 +202,7 @@ fun RepoContent(
repositoryState: RepositoryState, repositoryState: RepositoryState,
blameState: BlameState, blameState: BlameState,
showHistory: Boolean, showHistory: Boolean,
onShowSettingsDialog: () -> Unit,
) { ) {
if (showHistory) { if (showHistory) {
val historyViewModel = tabViewModel.historyViewModel val historyViewModel = tabViewModel.historyViewModel
@ -328,10 +222,42 @@ fun RepoContent(
selectedItem, selectedItem,
repositoryState, repositoryState,
blameState, blameState,
onShowSettingsDialog,
) )
} }
}
@Composable
fun SidePanelOption(title: String, icon: String, onClick: () -> Unit) {
Row(
modifier = Modifier
.height(36.dp)
.fillMaxWidth()
.handMouseClickable(onClick)
.padding(start = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painter = painterResource(icon),
contentDescription = null,
tint = MaterialTheme.colors.onBackground,
modifier = Modifier
.size(16.dp),
)
Text(
text = title,
modifier = Modifier
.padding(horizontal = 8.dp)
.weight(1f),
maxLines = 1,
style = MaterialTheme.typography.body2,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colors.onBackground,
overflow = TextOverflow.Ellipsis,
)
}
} }
@OptIn(ExperimentalSplitPaneApi::class) @OptIn(ExperimentalSplitPaneApi::class)
@ -341,18 +267,48 @@ fun MainContentView(
diffSelected: DiffEntryType?, diffSelected: DiffEntryType?,
selectedItem: SelectedItem, selectedItem: SelectedItem,
repositoryState: RepositoryState, repositoryState: RepositoryState,
blameState: BlameState blameState: BlameState,
) { onShowSettingsDialog: () -> Unit,
) {
HorizontalSplitPane( HorizontalSplitPane(
splitPaneState = rememberSplitPaneState(initialPositionPercentage = 0.20f) splitPaneState = rememberSplitPaneState(initialPositionPercentage = 0.20f)
) { ) {
first(minSize = 180.dp) { first(minSize = 180.dp) {
ScrollableColumn(modifier = Modifier.fillMaxHeight()) { Column {
Branches() val state: ScrollState = rememberScrollState()
Remotes()
Tags() val canBeScrolled by remember {
Stashes() derivedStateOf {
state.maxValue > 0
}
}
ScrollableColumn(
state = state,
modifier = Modifier
.weight(1f),
) {
Branches()
Remotes()
Tags()
Stashes()
// TODO: Enable on 1.2.0 when fully implemented Submodules() // TODO: Enable on 1.2.0 when fully implemented Submodules()
}
Column {
if (canBeScrolled) {
Box(
Modifier
.fillMaxWidth()
.height(2.dp)
.background(MaterialTheme.colors.onBackgroundSecondary.copy(alpha = 0.2f))
)
}
SidePanelOption("Open repository", "open.svg") { openRepositoryDialog(tabViewModel = tabViewModel) }
SidePanelOption("Refresh", "refresh.svg") { tabViewModel.refreshAll() }
SidePanelOption("Settings", "settings.svg", onShowSettingsDialog)
}
} }
} }

View File

@ -31,13 +31,12 @@ import com.jetpackduba.gitnuro.updates.Update
import com.jetpackduba.gitnuro.viewmodels.TabViewModel import com.jetpackduba.gitnuro.viewmodels.TabViewModel
@OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun WelcomePage( fun WelcomePage(
tabViewModel: TabViewModel, tabViewModel: TabViewModel,
onShowCloneDialog: () -> Unit,
) { ) {
val appStateManager = tabViewModel.appStateManager val appStateManager = tabViewModel.appStateManager
var showCloneView by remember { mutableStateOf(false) }
var showAdditionalInfo by remember { mutableStateOf(false) } var showAdditionalInfo by remember { mutableStateOf(false) }
var newUpdate by remember { mutableStateOf<Update?>(null) } var newUpdate by remember { mutableStateOf<Update?>(null) }
@ -62,7 +61,7 @@ fun WelcomePage(
HomeButtons( HomeButtons(
newUpdate = newUpdate, newUpdate = newUpdate,
tabViewModel = tabViewModel, tabViewModel = tabViewModel,
onShowCloneView = { showCloneView = true }, onShowCloneView = onShowCloneDialog,
onShowAdditionalInfo = { showAdditionalInfo = true }, onShowAdditionalInfo = { showAdditionalInfo = true },
) )
@ -79,24 +78,7 @@ fun WelcomePage(
) )
} }
LaunchedEffect(showCloneView) {
if (showCloneView) {
// tabViewModel.cloneViewModel.reset() // Reset dialog before showing it
}
}
if (showCloneView) {
CloneDialog(
// tabViewModel.cloneViewModel,
onClose = {
showCloneView = false
// tabViewModel.cloneViewModel.reset()
},
onOpenRepository = { dir ->
tabViewModel.openRepository(dir)
},
)
}
if (showAdditionalInfo) { if (showAdditionalInfo) {
AppInfoDialog( AppInfoDialog(

View File

@ -13,10 +13,9 @@ import com.jetpackduba.gitnuro.theme.scrollbarNormal
@Composable @Composable
fun ScrollableColumn( fun ScrollableColumn(
modifier: Modifier, modifier: Modifier,
state: ScrollState = rememberScrollState(0), state: ScrollState = rememberScrollState(),
content: @Composable ColumnScope.() -> Unit content: @Composable ColumnScope.() -> Unit
) { ) {
Box( Box(
modifier = modifier, modifier = modifier,
) { ) {

View File

@ -116,7 +116,7 @@ fun Log(
// the proper scroll position // the proper scroll position
verticalScrollState.observeScrollChanges() verticalScrollState.observeScrollChanges()
LaunchedEffect(verticalScrollState) { LaunchedEffect(verticalScrollState, commitList) {
launch { launch {
logViewModel.focusCommit.collect { commit -> logViewModel.focusCommit.collect { commit ->
scrollToCommit(verticalScrollState, commitList, commit) scrollToCommit(verticalScrollState, commitList, commit)