Created custom dialog UI

This commit is contained in:
Abdelilah El Aissaoui 2021-10-25 02:26:58 +02:00
parent e718f10b60
commit e6cd822b17
6 changed files with 375 additions and 229 deletions

View File

@ -1,26 +1,28 @@
package app package app
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.* import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
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.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.DefaultAlpha import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.* import androidx.compose.ui.window.*
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import app.di.DaggerAppComponent import app.di.DaggerAppComponent
import app.git.GitManager import app.git.GitManager
import app.git.RepositorySelectionStatus
import app.theme.AppTheme import app.theme.AppTheme
import app.ui.RepositoryOpenPage import app.ui.AppTab
import app.ui.WelcomePage import app.ui.components.DialogBox
import app.ui.components.RepositoriesTabPanel import app.ui.components.RepositoriesTabPanel
import app.ui.components.TabInformation import app.ui.components.TabInformation
import javax.inject.Inject import javax.inject.Inject
@ -56,16 +58,67 @@ class Main {
) )
) { ) {
AppTheme { AppTheme {
val showDialog = remember { mutableStateOf(false) }
val dialogManager = remember { DialogManager(showDialog) }
Box {
AppTabs(dialogManager)
if (showDialog.value) {
val interactionSource = remember { MutableInteractionSource() }
Box(
modifier = Modifier
.fillMaxSize()
.alpha(0.8f)
.background(Color.Black)
.clickable(
enabled = true,
onClick = {},
interactionSource = interactionSource,
indication = null
)
)
DialogBox(
modifier = Modifier
.align(Alignment.Center)
.clickable(
enabled = true,
onClick = {},
interactionSource = interactionSource,
indication = null
)
) {
dialogManager.dialog()
}
}
}
}
}
}
}
@Composable
fun AppTabs(dialogManager: DialogManager) {
val tabs = remember { val tabs = remember {
val repositoriesSavedTabs = appStateManager.openRepositoriesPathsTabs val repositoriesSavedTabs = appStateManager.openRepositoriesPathsTabs
var repoTabs = repositoriesSavedTabs.map { repositoryTab -> var repoTabs = repositoriesSavedTabs.map { repositoryTab ->
newAppTab(key = repositoryTab.key, path = repositoryTab.value) newAppTab(
dialogManager = dialogManager,
key = repositoryTab.key,
path = repositoryTab.value
)
} }
if (repoTabs.isEmpty()) { if (repoTabs.isEmpty()) {
repoTabs = listOf( repoTabs = listOf(
newAppTab() newAppTab(
dialogManager = dialogManager
)
) )
} }
@ -73,6 +126,7 @@ class Main {
} }
var selectedTabKey by remember { mutableStateOf(0) } var selectedTabKey by remember { mutableStateOf(0) }
Column( Column(
modifier = modifier =
Modifier.background(MaterialTheme.colors.surface) Modifier.background(MaterialTheme.colors.surface)
@ -92,7 +146,10 @@ class Main {
selectedTabKey = newSelectedTabKey selectedTabKey = newSelectedTabKey
}, },
newTabContent = { key -> newTabContent = { key ->
newAppTab(key) newAppTab(
dialogManager = dialogManager,
key = key
)
}, },
onTabsUpdated = { tabInformationList -> onTabsUpdated = { tabInformationList ->
tabs.value = tabInformationList tabs.value = tabInformationList
@ -141,11 +198,9 @@ class Main {
} }
} }
} }
}
}
}
private fun newAppTab( private fun newAppTab(
dialogManager: DialogManager,
key: Int = 0, key: Int = 0,
tabName: MutableState<String> = mutableStateOf("New tab"), tabName: MutableState<String> = mutableStateOf("New tab"),
path: String? = null, path: String? = null,
@ -163,63 +218,26 @@ class Main {
appStateManager.repositoryTabChanged(key, path) appStateManager.repositoryTabChanged(key, path)
} }
App(gitManager, path, tabName) AppTab(gitManager, dialogManager, path, tabName)
} }
} }
} }
class DialogManager(private val showDialog: MutableState<Boolean>) {
private var content: @Composable () -> Unit = {}
fun show(content: @Composable () -> Unit) {
this.content = content
showDialog.value = true
}
fun dismiss() {
showDialog.value = false
}
@Composable @Composable
fun App(gitManager: GitManager, repositoryPath: String?, tabName: MutableState<String>) { fun dialog() {
LaunchedEffect(gitManager) { content()
if (repositoryPath != null)
gitManager.openRepository(repositoryPath)
}
val repositorySelectionStatus by gitManager.repositorySelectionStatus.collectAsState()
val isProcessing by gitManager.processing.collectAsState()
if (repositorySelectionStatus is RepositorySelectionStatus.Open) {
tabName.value = gitManager.repositoryName
}
Column(
modifier = Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize()
) {
val linearProgressAlpha = if (isProcessing)
DefaultAlpha
else
0f
LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.alpha(linearProgressAlpha)
)
Box(modifier = Modifier.fillMaxSize()) {
Crossfade(targetState = repositorySelectionStatus) {
@Suppress("UnnecessaryVariable") // Don't inline it because smart cast won't work
when (repositorySelectionStatus) {
RepositorySelectionStatus.None -> {
WelcomePage(gitManager = gitManager)
}
RepositorySelectionStatus.Loading -> {
LoadingRepository()
}
is RepositorySelectionStatus.Open -> {
RepositoryOpenPage(gitManager = gitManager)
}
}
}
if (isProcessing)
Box(modifier = Modifier.fillMaxSize()) //TODO this should block of the mouse/keyboard events while visible
}
} }
} }

View File

@ -1,12 +1,8 @@
package app.git.dialogs package app.git.dialogs
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.*
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.OutlinedTextField
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
@ -15,8 +11,6 @@ import androidx.compose.ui.focus.focusOrder
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.rememberDialogState
@Composable @Composable
fun NewBranchDialog( fun NewBranchDialog(
@ -24,37 +18,48 @@ fun NewBranchDialog(
onAccept: (branchName: String) -> Unit onAccept: (branchName: String) -> Unit
) { ) {
var branchField by remember { mutableStateOf("") } var branchField by remember { mutableStateOf("") }
val userFieldFocusRequester = remember { FocusRequester() } val branchFieldFocusRequester = remember { FocusRequester() }
val buttonFieldFocusRequester = remember { FocusRequester() } val buttonFieldFocusRequester = remember { FocusRequester() }
Dialog(
state = rememberDialogState(width = 0.dp, height = 0.dp),
onCloseRequest = onReject,
title = "",
) {
Column( Column(
modifier = Modifier.fillMaxSize(), modifier = Modifier
.background(MaterialTheme.colors.background),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
) { ) {
Text("Enter a branch name")
OutlinedTextField( OutlinedTextField(
modifier = Modifier.focusOrder(userFieldFocusRequester) { modifier = Modifier
.focusOrder(branchFieldFocusRequester) {
this.next = buttonFieldFocusRequester this.next = buttonFieldFocusRequester
}, }
.width(300.dp),
value = branchField, value = branchField,
label = { Text("User", fontSize = 14.sp) }, singleLine = true,
label = { Text("New branch name", fontSize = 14.sp) },
textStyle = TextStyle(fontSize = 14.sp), textStyle = TextStyle(fontSize = 14.sp),
onValueChange = { onValueChange = {
branchField = it branchField = it
}, },
) )
Row(
modifier = Modifier
.padding(top = 16.dp)
.align(Alignment.End)
) {
TextButton(
modifier = Modifier.padding(end = 8.dp),
onClick = {
onReject()
}
) {
Text("Cancel")
}
Button( Button(
modifier = Modifier.focusOrder(buttonFieldFocusRequester) { modifier = Modifier.focusOrder(buttonFieldFocusRequester) {
this.previous = userFieldFocusRequester this.previous = branchFieldFocusRequester
this.next = userFieldFocusRequester this.next = branchFieldFocusRequester
}, },
enabled = branchField.isNotEmpty(),
onClick = { onClick = {
onAccept(branchField) onAccept(branchField)
} }

View File

@ -1,12 +1,8 @@
package app.git.dialogs package app.git.dialogs
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.*
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.OutlinedTextField
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
@ -29,23 +25,27 @@ fun UserPasswordDialog(
val passwordFieldFocusRequester = remember { FocusRequester() } val passwordFieldFocusRequester = remember { FocusRequester() }
val buttonFieldFocusRequester = remember { FocusRequester() } val buttonFieldFocusRequester = remember { FocusRequester() }
Dialog(
onCloseRequest = onReject,
title = "",
) {
Column( Column(
modifier = Modifier.fillMaxSize(), modifier = Modifier
.background(MaterialTheme.colors.background),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
) { ) {
Text("Introduce your remote server credentials")
Text(
text = "Introduce your remote server credentials",
modifier = Modifier
.padding(vertical = 8.dp),
)
OutlinedTextField( OutlinedTextField(
modifier = Modifier.focusOrder(userFieldFocusRequester) { modifier = Modifier
.focusOrder(userFieldFocusRequester) {
this.next = passwordFieldFocusRequester this.next = passwordFieldFocusRequester
}, }
.width(300.dp),
value = userField, value = userField,
singleLine = true,
label = { Text("User", fontSize = 14.sp) }, label = { Text("User", fontSize = 14.sp) },
textStyle = TextStyle(fontSize = 14.sp), textStyle = TextStyle(fontSize = 14.sp),
onValueChange = { onValueChange = {
@ -57,8 +57,10 @@ fun UserPasswordDialog(
.focusOrder(passwordFieldFocusRequester) { .focusOrder(passwordFieldFocusRequester) {
this.previous = userFieldFocusRequester this.previous = userFieldFocusRequester
this.next = buttonFieldFocusRequester this.next = buttonFieldFocusRequester
}, }
.width(300.dp),
value = passwordField, value = passwordField,
singleLine = true,
label = { Text("Password", fontSize = 14.sp) }, label = { Text("Password", fontSize = 14.sp) },
textStyle = TextStyle(fontSize = 14.sp), textStyle = TextStyle(fontSize = 14.sp),
onValueChange = { onValueChange = {
@ -66,6 +68,20 @@ fun UserPasswordDialog(
}, },
visualTransformation = PasswordVisualTransformation() visualTransformation = PasswordVisualTransformation()
) )
Row(
modifier = Modifier
.padding(top = 16.dp)
.align(Alignment.End)
) {
TextButton(
modifier = Modifier.padding(end = 8.dp),
onClick = {
onReject()
}
) {
Text("Cancel")
}
Button( Button(
modifier = Modifier.focusOrder(buttonFieldFocusRequester) { modifier = Modifier.focusOrder(buttonFieldFocusRequester) {
this.previous = passwordFieldFocusRequester this.previous = passwordFieldFocusRequester
@ -78,5 +94,6 @@ fun UserPasswordDialog(
Text("Ok") Text("Ok")
} }
} }
} }
} }

View File

@ -0,0 +1,74 @@
package app.ui
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.DefaultAlpha
import app.DialogManager
import app.LoadingRepository
import app.git.GitManager
import app.git.RepositorySelectionStatus
@Composable
fun AppTab(gitManager: GitManager, dialogManager: DialogManager, repositoryPath: String?, tabName: MutableState<String>) {
LaunchedEffect(gitManager) {
if (repositoryPath != null)
gitManager.openRepository(repositoryPath)
}
val repositorySelectionStatus by gitManager.repositorySelectionStatus.collectAsState()
val isProcessing by gitManager.processing.collectAsState()
if (repositorySelectionStatus is RepositorySelectionStatus.Open) {
tabName.value = gitManager.repositoryName
}
Column(
modifier = Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize()
) {
val linearProgressAlpha = if (isProcessing)
DefaultAlpha
else
0f
LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.alpha(linearProgressAlpha)
)
Box(modifier = Modifier.fillMaxSize()) {
Crossfade(targetState = repositorySelectionStatus) {
@Suppress("UnnecessaryVariable") // Don't inline it because smart cast won't work
when (repositorySelectionStatus) {
RepositorySelectionStatus.None -> {
WelcomePage(gitManager = gitManager)
}
RepositorySelectionStatus.Loading -> {
LoadingRepository()
}
is RepositorySelectionStatus.Open -> {
RepositoryOpenPage(gitManager = gitManager, dialogManager = dialogManager)
}
}
}
if (isProcessing)
Box(modifier = Modifier.fillMaxSize()) //TODO this should block of the mouse/keyboard events while visible
}
}
}

View File

@ -11,6 +11,7 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import app.DialogManager
import app.credentials.CredentialsState import app.credentials.CredentialsState
import app.git.DiffEntryType import app.git.DiffEntryType
import app.git.GitManager import app.git.GitManager
@ -21,7 +22,7 @@ import org.eclipse.jgit.revwalk.RevCommit
@Composable @Composable
fun RepositoryOpenPage(gitManager: GitManager) { fun RepositoryOpenPage(gitManager: GitManager, dialogManager: DialogManager) {
var selectedRevCommit by remember { var selectedRevCommit by remember {
mutableStateOf<RevCommit?>(null) mutableStateOf<RevCommit?>(null)
} }
@ -33,34 +34,27 @@ fun RepositoryOpenPage(gitManager: GitManager) {
mutableStateOf(false) mutableStateOf(false)
} }
var showBranchDialog by remember { // var showBranchDialog by remember {
mutableStateOf(false) // mutableStateOf(false)
} // }
val selectedIndexCommitLog = remember { mutableStateOf(-1) } val selectedIndexCommitLog = remember { mutableStateOf(-1) }
val credentialsState by gitManager.credentialsState.collectAsState() val credentialsState by gitManager.credentialsState.collectAsState()
if (credentialsState == CredentialsState.CredentialsRequested) { if (credentialsState == CredentialsState.CredentialsRequested) {
dialogManager.show {
UserPasswordDialog( UserPasswordDialog(
onReject = { onReject = {
gitManager.credentialsDenied() gitManager.credentialsDenied()
dialogManager.dismiss()
}, },
onAccept = { user, password -> onAccept = { user, password ->
gitManager.credentialsAccepted(user, password) gitManager.credentialsAccepted(user, password)
dialogManager.dismiss()
} }
) )
} }
if (showBranchDialog) {
NewBranchDialog(
onReject = {
showBranchDialog = false
},
onAccept = { branchName ->
gitManager.createBranch(branchName)
}
)
} }
Column { Column {
@ -72,7 +66,19 @@ fun RepositoryOpenPage(gitManager: GitManager) {
onPush = { gitManager.push() }, onPush = { gitManager.push() },
onStash = { gitManager.stash() }, onStash = { gitManager.stash() },
onPopStash = { gitManager.popStash() }, onPopStash = { gitManager.popStash() },
onCreateBranch = { showBranchDialog = true } onCreateBranch = {
dialogManager.show {
NewBranchDialog(
onReject = {
dialogManager.dismiss()
},
onAccept = { branchName ->
gitManager.createBranch(branchName)
dialogManager.dismiss()
}
)
}
}
) )
Row { Row {

View File

@ -0,0 +1,26 @@
package app.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
@Composable
fun DialogBox(
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
Box(
modifier = modifier
.clip(RoundedCornerShape(5))
.background(MaterialTheme.colors.background)
.padding(16.dp)
) {
content()
}
}