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.LocalTabScope
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.UserPasswordDialog
import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog
import com.jetpackduba.gitnuro.viewmodels.RepositorySelectionStatus
import com.jetpackduba.gitnuro.viewmodels.TabViewModel
import kotlinx.coroutines.delay
@ -58,11 +60,34 @@ fun AppTab(
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()) {
Crossfade(targetState = repositorySelectionStatus) {
when (repositorySelectionStatusValue) {
RepositorySelectionStatus.None -> {
WelcomePage(tabViewModel = tabViewModel)
WelcomePage(
tabViewModel = tabViewModel,
onShowCloneDialog = { showSettingsDialog = true }
)
}
is RepositorySelectionStatus.Opening -> {
@ -70,7 +95,11 @@ fun AppTab(
}
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
import androidx.compose.foundation.ScrollState
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.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
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.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.layout.onSizeChanged
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.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.git.DiffEntryType
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
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.dialogs.*
import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog
import com.jetpackduba.gitnuro.ui.diff.Diff
import com.jetpackduba.gitnuro.ui.log.Log
import com.jetpackduba.gitnuro.viewmodels.BlameState
@ -52,7 +44,11 @@ import org.jetbrains.compose.splitpane.rememberSplitPaneState
import java.awt.Cursor
@Composable
fun RepositoryOpenPage(tabViewModel: TabViewModel) {
fun RepositoryOpenPage(
tabViewModel: TabViewModel,
onShowSettingsDialog: () -> Unit,
onShowCloneDialog: () -> Unit,
) {
val repositoryState by tabViewModel.repositoryState.collectAsState()
val diffSelected by tabViewModel.diffSelected.collectAsState()
val selectedItem by tabViewModel.selectedItem.collectAsState()
@ -101,7 +97,7 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
showQuickActionsDialog = false
when (it) {
QuickActionType.OPEN_DIR_IN_FILE_MANAGER -> tabViewModel.openFolderInFileExplorer()
QuickActionType.CLONE -> TODO()
QuickActionType.CLONE -> onShowCloneDialog()
}
},
)
@ -114,8 +110,6 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
}
Column {
Row(modifier = Modifier.weight(1f)) {
SideBar(tabViewModel)
Column(
modifier = Modifier
.focusRequester(focusRequester)
@ -148,7 +142,15 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
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
private fun BottomInfoBar(tabViewModel: TabViewModel) {
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
fun RepoContent(
tabViewModel: TabViewModel,
@ -309,6 +202,7 @@ fun RepoContent(
repositoryState: RepositoryState,
blameState: BlameState,
showHistory: Boolean,
onShowSettingsDialog: () -> Unit,
) {
if (showHistory) {
val historyViewModel = tabViewModel.historyViewModel
@ -328,10 +222,42 @@ fun RepoContent(
selectedItem,
repositoryState,
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)
@ -341,19 +267,49 @@ fun MainContentView(
diffSelected: DiffEntryType?,
selectedItem: SelectedItem,
repositoryState: RepositoryState,
blameState: BlameState
) {
blameState: BlameState,
onShowSettingsDialog: () -> Unit,
) {
HorizontalSplitPane(
splitPaneState = rememberSplitPaneState(initialPositionPercentage = 0.20f)
) {
first(minSize = 180.dp) {
ScrollableColumn(modifier = Modifier.fillMaxHeight()) {
Column {
val state: ScrollState = rememberScrollState()
val canBeScrolled by remember {
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()
}
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)
}
}
}
splitter {

View File

@ -31,13 +31,12 @@ import com.jetpackduba.gitnuro.updates.Update
import com.jetpackduba.gitnuro.viewmodels.TabViewModel
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun WelcomePage(
tabViewModel: TabViewModel,
onShowCloneDialog: () -> Unit,
) {
val appStateManager = tabViewModel.appStateManager
var showCloneView by remember { mutableStateOf(false) }
var showAdditionalInfo by remember { mutableStateOf(false) }
var newUpdate by remember { mutableStateOf<Update?>(null) }
@ -62,7 +61,7 @@ fun WelcomePage(
HomeButtons(
newUpdate = newUpdate,
tabViewModel = tabViewModel,
onShowCloneView = { showCloneView = true },
onShowCloneView = onShowCloneDialog,
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) {
AppInfoDialog(

View File

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

View File

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