Added new side bar

This commit is contained in:
Abdelilah El Aissaoui 2022-10-05 18:55:53 +02:00
parent c6b0250a63
commit 2ffb8d9038
17 changed files with 252 additions and 172 deletions

View File

@ -4,16 +4,12 @@ package com.jetpackduba.gitnuro
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
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.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerIconDefaults
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Density
@ -35,8 +31,6 @@ import com.jetpackduba.gitnuro.theme.secondaryTextColor
import com.jetpackduba.gitnuro.ui.AppTab import com.jetpackduba.gitnuro.ui.AppTab
import com.jetpackduba.gitnuro.ui.components.RepositoriesTabPanel import com.jetpackduba.gitnuro.ui.components.RepositoriesTabPanel
import com.jetpackduba.gitnuro.ui.components.TabInformation import com.jetpackduba.gitnuro.ui.components.TabInformation
import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog
import com.jetpackduba.gitnuro.viewmodels.SettingsViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -53,9 +47,6 @@ class App {
@Inject @Inject
lateinit var appSettings: AppSettings lateinit var appSettings: AppSettings
@Inject
lateinit var settingsViewModel: SettingsViewModel
init { init {
appComponent.inject(this) appComponent.inject(this)
} }
@ -107,25 +98,12 @@ class App {
emptyArray() emptyArray()
CompositionLocalProvider(values = density) { CompositionLocalProvider(values = density) {
var showSettingsDialog by remember { mutableStateOf(false) }
AppTheme( AppTheme(
selectedTheme = theme, selectedTheme = theme,
customTheme = customTheme, customTheme = customTheme,
) { ) {
Box(modifier = Modifier.background(MaterialTheme.colors.background)) { Box(modifier = Modifier.background(MaterialTheme.colors.background)) {
AppTabs( AppTabs()
onOpenSettings = {
showSettingsDialog = true
}
)
}
if (showSettingsDialog) {
SettingsDialog(
settingsViewModel = settingsViewModel,
onDismiss = { showSettingsDialog = false }
)
} }
} }
} }
@ -160,9 +138,7 @@ class App {
@Composable @Composable
fun AppTabs( fun AppTabs() {
onOpenSettings: () -> Unit,
) {
val tabs by tabsFlow.collectAsState() val tabs by tabsFlow.collectAsState()
val tabsInformationList = tabs.sortedBy { it.key } val tabsInformationList = tabs.sortedBy { it.key }
val selectedTabKey = remember { mutableStateOf(0) } val selectedTabKey = remember { mutableStateOf(0) }
@ -173,7 +149,6 @@ class App {
Tabs( Tabs(
tabsInformationList = tabsInformationList, tabsInformationList = tabsInformationList,
selectedTabKey = selectedTabKey, selectedTabKey = selectedTabKey,
onOpenSettings = onOpenSettings,
onAddedTab = { tabInfo -> onAddedTab = { tabInfo ->
addTab(tabInfo) addTab(tabInfo)
}, },
@ -203,11 +178,9 @@ class App {
tabsFlow.value = tabsFlow.value.toMutableList().apply { add(tabInformation) } tabsFlow.value = tabsFlow.value.toMutableList().apply { add(tabInformation) }
} }
@OptIn(ExperimentalComposeUiApi::class)
@Composable @Composable
fun Tabs( fun Tabs(
selectedTabKey: MutableState<Int>, selectedTabKey: MutableState<Int>,
onOpenSettings: () -> Unit,
tabsInformationList: List<TabInformation>, tabsInformationList: List<TabInformation>,
onAddedTab: (TabInformation) -> Unit, onAddedTab: (TabInformation) -> Unit,
onRemoveTab: (Int) -> Unit, onRemoveTab: (Int) -> Unit,
@ -237,20 +210,6 @@ class App {
}, },
onTabClosed = onRemoveTab onTabClosed = onRemoveTab
) )
IconButton(
modifier = Modifier
.padding(horizontal = 8.dp)
.size(24.dp)
.pointerHoverIcon(PointerIconDefaults.Hand),
onClick = onOpenSettings
) {
Icon(
painter = painterResource("settings.svg"),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
tint = MaterialTheme.colors.primaryVariant,
)
}
} }
} }

View File

@ -3,6 +3,7 @@ package com.jetpackduba.gitnuro.di
import com.jetpackduba.gitnuro.App import com.jetpackduba.gitnuro.App
import com.jetpackduba.gitnuro.AppStateManager import com.jetpackduba.gitnuro.AppStateManager
import com.jetpackduba.gitnuro.preferences.AppSettings import com.jetpackduba.gitnuro.preferences.AppSettings
import com.jetpackduba.gitnuro.viewmodels.SettingsViewModel
import dagger.Component import dagger.Component
import javax.inject.Singleton import javax.inject.Singleton
@ -11,6 +12,7 @@ import javax.inject.Singleton
interface AppComponent { interface AppComponent {
fun inject(main: App) fun inject(main: App)
fun appStateManager(): AppStateManager fun appStateManager(): AppStateManager
fun settingsViewModel(): SettingsViewModel
fun appPreferences(): AppSettings fun appPreferences(): AppSettings
} }

View File

@ -17,11 +17,10 @@ fun Modifier.backgroundIf(condition: Boolean, color: Color): Modifier {
} }
} }
@OptIn(ExperimentalComposeUiApi::class)
fun Modifier.handMouseClickable(onClick: () -> Unit): Modifier { fun Modifier.handMouseClickable(onClick: () -> Unit): Modifier {
return this return this
.clickable { onClick() } .clickable { onClick() }
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
} }
/** /**
@ -30,4 +29,9 @@ fun Modifier.handMouseClickable(onClick: () -> Unit): Modifier {
*/ */
fun Modifier.ignoreKeyEvents(): Modifier { fun Modifier.ignoreKeyEvents(): Modifier {
return this.onPreviewKeyEvent { true } return this.onPreviewKeyEvent { true }
}
@OptIn(ExperimentalComposeUiApi::class)
fun Modifier.handOnHover(): Modifier {
return this.pointerHoverIcon(PointerIconDefaults.Hand)
} }

View File

@ -31,7 +31,7 @@ val lightTheme = ColorsScheme(
val darkBlueTheme = ColorsScheme( val darkBlueTheme = ColorsScheme(
primary = Color(0xFF014F97), primary = Color(0xFF1074BC),
primaryVariant = Color(0xFF9FD1FF), primaryVariant = Color(0xFF9FD1FF),
onPrimary = Color(0xFFFFFFFFF), onPrimary = Color(0xFFFFFFFFF),
secondary = Color(0xFFe9c754), secondary = Color(0xFFe9c754),
@ -39,10 +39,10 @@ val darkBlueTheme = ColorsScheme(
secondaryText = Color(0xFFCCCBCB), secondaryText = Color(0xFFCCCBCB),
error = Color(0xFFc93838), error = Color(0xFFc93838),
onError = Color(0xFFFFFFFF), onError = Color(0xFFFFFFFF),
background = Color(0xFF0E1621), background = Color(0xFF16181F),
backgroundSelected = Color(0xFF2f3640), backgroundSelected = Color(0xFF2f3640),
surface = Color(0xFF15212d), surface = Color(0xFF202538),
secondarySurface = Color(0xFF1a2c40), secondarySurface = Color(0xFF122C46),
headerBackground = Color(0xFF0a335c), headerBackground = Color(0xFF0a335c),
borderColor = Color(0xFF989898), borderColor = Color(0xFF989898),
addFile = Color(0xFF32A852), addFile = Color(0xFF32A852),

View File

@ -27,6 +27,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.jetpackduba.gitnuro.extensions.handMouseClickable import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.extensions.lineAt import com.jetpackduba.gitnuro.extensions.lineAt
import com.jetpackduba.gitnuro.extensions.toStringWithSpaces import com.jetpackduba.gitnuro.extensions.toStringWithSpaces
import com.jetpackduba.gitnuro.keybindings.KeybindingOption import com.jetpackduba.gitnuro.keybindings.KeybindingOption
@ -187,7 +188,7 @@ fun MinimizedBlame(
onClick = onClose, onClick = onClose,
modifier = Modifier modifier = Modifier
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
) { ) {
Image( Image(
painter = painterResource("close.svg"), painter = painterResource("close.svg"),
@ -222,7 +223,7 @@ private fun Header(
IconButton( IconButton(
onClick = onClose, onClick = onClose,
modifier = Modifier modifier = Modifier
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
) { ) {
Image( Image(
painter = painterResource("close.svg"), painter = painterResource("close.svg"),

View File

@ -24,6 +24,7 @@ import androidx.compose.ui.input.pointer.pointerHoverIcon
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 com.jetpackduba.gitnuro.extensions.handMouseClickable import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.extensions.toSmartSystemString import com.jetpackduba.gitnuro.extensions.toSmartSystemString
import com.jetpackduba.gitnuro.git.diff.DiffResult import com.jetpackduba.gitnuro.git.diff.DiffResult
import com.jetpackduba.gitnuro.keybindings.KeybindingOption import com.jetpackduba.gitnuro.keybindings.KeybindingOption
@ -102,7 +103,7 @@ private fun Header(
IconButton( IconButton(
onClick = onClose, onClick = onClose,
modifier = Modifier modifier = Modifier
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
) { ) {
Image( Image(
painter = painterResource("close.svg"), painter = painterResource("close.svg"),

View File

@ -6,6 +6,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.ArrowDropDown
@ -13,6 +14,8 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
@ -22,6 +25,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.jetpackduba.gitnuro.extensions.handMouseClickable import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.extensions.ignoreKeyEvents import com.jetpackduba.gitnuro.extensions.ignoreKeyEvents
import com.jetpackduba.gitnuro.theme.primaryTextColor import com.jetpackduba.gitnuro.theme.primaryTextColor
import com.jetpackduba.gitnuro.ui.context_menu.* import com.jetpackduba.gitnuro.ui.context_menu.*
@ -32,7 +36,6 @@ import com.jetpackduba.gitnuro.viewmodels.MenuViewModel
fun Menu( fun Menu(
modifier: Modifier, modifier: Modifier,
menuViewModel: MenuViewModel, menuViewModel: MenuViewModel,
onRepositoryOpen: () -> Unit,
onCreateBranch: () -> Unit, onCreateBranch: () -> Unit,
onStashWithMessage: () -> Unit, onStashWithMessage: () -> Unit,
) { ) {
@ -43,15 +46,6 @@ fun Menu(
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
MenuButton(
modifier = Modifier.padding(start = 8.dp),
title = "Open",
icon = painterResource("open.svg"),
onClick = {
onRepositoryOpen()
}
)
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
ExtendedMenuButton( ExtendedMenuButton(
@ -129,7 +123,6 @@ fun Menu(
val menuOptions = remember { val menuOptions = remember {
repositoryAdditionalOptionsMenu( repositoryAdditionalOptionsMenu(
onOpenRepositoryOnFileExplorer = { menuViewModel.openFolderInFileExplorer() }, onOpenRepositoryOnFileExplorer = { menuViewModel.openFolderInFileExplorer() },
onForceRepositoryRefresh = { menuViewModel.refresh() },
) )
} }
for (item in menuOptions) { for (item in menuOptions) {
@ -153,34 +146,31 @@ fun MenuButton(
icon: Painter, icon: Painter,
onClick: () -> Unit onClick: () -> Unit
) { ) {
val iconColor = if (enabled) { Row(
MaterialTheme.colors.primaryVariant
} else {
MaterialTheme.colors.secondaryVariant //todo this color isn't specified anywhere
}
Column(
modifier = modifier modifier = modifier
.ignoreKeyEvents() .ignoreKeyEvents()
.clip(RoundedCornerShape(4.dp))
.background(MaterialTheme.colors.primary)
.handMouseClickable { if (enabled) onClick() } .handMouseClickable { if (enabled) onClick() }
.padding(vertical = 4.dp, horizontal = 16.dp) .width(100.dp),
.width(48.dp), horizontalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally, verticalAlignment = Alignment.CenterVertically,
verticalArrangement = Arrangement.Center,
) { ) {
Image( Icon(
painter = icon, painter = icon,
contentDescription = title, contentDescription = title,
modifier = Modifier modifier = Modifier
.padding(vertical = 2.dp) .padding(vertical = 2.dp)
.size(24.dp), .size(24.dp),
colorFilter = ColorFilter.tint(iconColor), tint = MaterialTheme.colors.onPrimary,
) )
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.body2, style = MaterialTheme.typography.body2,
modifier = Modifier.padding(start = 2.dp, top = 8.dp, bottom = 8.dp),
maxLines = 1, maxLines = 1,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
color = MaterialTheme.colors.onPrimary,
) )
} }
} }
@ -194,44 +184,40 @@ fun ExtendedMenuButton(
onClick: () -> Unit, onClick: () -> Unit,
extendedListItems: List<DropDownContentData>, extendedListItems: List<DropDownContentData>,
) { ) {
val iconColor = if (enabled) {
MaterialTheme.colors.primaryVariant
} else {
MaterialTheme.colors.secondaryVariant
}
var showDropDownMenu by remember { mutableStateOf(false) } var showDropDownMenu by remember { mutableStateOf(false) }
Row( Row(
modifier = modifier modifier = modifier
.height(IntrinsicSize.Min) .height(IntrinsicSize.Min)
.ignoreKeyEvents() .ignoreKeyEvents()
.handMouseClickable { if (enabled) onClick() } .clip(RoundedCornerShape(4.dp))
.background(MaterialTheme.colors.primary)
) { ) {
Column( Row(
modifier = Modifier modifier = Modifier
.padding(top = 4.dp, bottom = 4.dp, start = 16.dp, end = 4.dp) .width(92.dp)
.width(40.dp), .handMouseClickable { if (enabled) onClick() },
horizontalAlignment = Alignment.CenterHorizontally, horizontalArrangement = Arrangement.Center,
verticalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically,
) { ) {
Image( Icon(
painter = icon, painter = icon,
contentDescription = title, contentDescription = title,
modifier = Modifier modifier = Modifier
.padding(vertical = 2.dp)
.size(24.dp), .size(24.dp),
colorFilter = ColorFilter.tint(iconColor), tint = MaterialTheme.colors.onPrimary,
) )
Text( Text(
text = title, text = title,
modifier = Modifier.padding(start = 2.dp, top = 8.dp, bottom = 8.dp),
style = MaterialTheme.typography.body2, style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onPrimary,
) )
} }
Box( Box(
modifier = Modifier modifier = Modifier
.width(20.dp) .width(24.dp)
.fillMaxHeight() .fillMaxHeight()
.ignoreKeyEvents() .ignoreKeyEvents()
.handMouseClickable { .handMouseClickable {
@ -239,10 +225,18 @@ fun ExtendedMenuButton(
}, },
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
Box(
modifier = Modifier
.fillMaxHeight()
.background(MaterialTheme.colors.onPrimary.copy(alpha = 0.5f))
.width(1.dp)
.align(Alignment.CenterStart)
)
Icon( Icon(
Icons.Default.ArrowDropDown, Icons.Default.ArrowDropDown,
contentDescription = null, contentDescription = null,
tint = MaterialTheme.colors.primaryTextColor, tint = MaterialTheme.colors.onPrimary,
) )
DropdownMenu( DropdownMenu(
@ -275,7 +269,7 @@ fun IconMenuButton(
IconButton( IconButton(
modifier = modifier modifier = modifier
.pointerHoverIcon(PointerIconDefaults.Hand), .handOnHover(),
enabled = enabled, enabled = enabled,
onClick = onClick, onClick = onClick,
) { ) {

View File

@ -16,6 +16,7 @@ import androidx.compose.ui.input.pointer.PointerIconDefaults
import androidx.compose.ui.input.pointer.pointerHoverIcon import androidx.compose.ui.input.pointer.pointerHoverIcon
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 com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.extensions.simpleName import com.jetpackduba.gitnuro.extensions.simpleName
import com.jetpackduba.gitnuro.theme.primaryTextColor import com.jetpackduba.gitnuro.theme.primaryTextColor
import com.jetpackduba.gitnuro.ui.components.SideMenuPanel import com.jetpackduba.gitnuro.ui.components.SideMenuPanel
@ -29,7 +30,6 @@ import com.jetpackduba.gitnuro.viewmodels.RemoteView
import com.jetpackduba.gitnuro.viewmodels.RemotesViewModel import com.jetpackduba.gitnuro.viewmodels.RemotesViewModel
import org.eclipse.jgit.lib.Ref import org.eclipse.jgit.lib.Ref
@OptIn(ExperimentalComposeUiApi::class)
@Composable @Composable
fun Remotes( fun Remotes(
remotesViewModel: RemotesViewModel, remotesViewModel: RemotesViewModel,
@ -62,7 +62,7 @@ fun Remotes(
modifier = Modifier modifier = Modifier
.padding(end = 16.dp) .padding(end = 16.dp)
.size(16.dp) .size(16.dp)
.pointerHoverIcon(PointerIconDefaults.Hand), .handOnHover(),
) { ) {
Icon( Icon(
painter = painterResource("settings.svg"), painter = painterResource("settings.svg"),

View File

@ -4,27 +4,44 @@ package com.jetpackduba.gitnuro.ui
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.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.unit.dp import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.res.painterResource
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.primaryTextColor
import com.jetpackduba.gitnuro.theme.secondarySurface
import com.jetpackduba.gitnuro.ui.components.ScrollableColumn import com.jetpackduba.gitnuro.ui.components.ScrollableColumn
import com.jetpackduba.gitnuro.ui.dialogs.AuthorDialog import com.jetpackduba.gitnuro.ui.dialogs.AuthorDialog
import com.jetpackduba.gitnuro.ui.dialogs.NewBranchDialog import com.jetpackduba.gitnuro.ui.dialogs.NewBranchDialog
import com.jetpackduba.gitnuro.ui.dialogs.StashWithMessageDialog import com.jetpackduba.gitnuro.ui.dialogs.StashWithMessageDialog
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
@ -44,7 +61,6 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
val selectedItem by tabViewModel.selectedItem.collectAsState() val selectedItem by tabViewModel.selectedItem.collectAsState()
val blameState by tabViewModel.blameState.collectAsState() val blameState by tabViewModel.blameState.collectAsState()
val showHistory by tabViewModel.showHistory.collectAsState() val showHistory by tabViewModel.showHistory.collectAsState()
val userInfo by tabViewModel.authorInfoSimple.collectAsState()
val showAuthorInfo by tabViewModel.showAuthorInfo.collectAsState() val showAuthorInfo by tabViewModel.showAuthorInfo.collectAsState()
var showNewBranchDialog by remember { mutableStateOf(false) } var showNewBranchDialog by remember { mutableStateOf(false) }
@ -87,40 +103,45 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
LaunchedEffect(selectedItem) { LaunchedEffect(selectedItem) {
focusRequester.requestFocus() focusRequester.requestFocus()
} }
Column {
Row(modifier = Modifier.weight(1f)) {
SideBar(tabViewModel)
Column( Column(
modifier = Modifier modifier = Modifier
.focusRequester(focusRequester) .focusRequester(focusRequester)
.focusable() .focusable()
.onKeyEvent { keyEvent -> .onKeyEvent { keyEvent ->
if (keyEvent.matchesBinding(KeybindingOption.REFRESH)) { if (keyEvent.matchesBinding(KeybindingOption.REFRESH)) {
tabViewModel.refreshAll() tabViewModel.refreshAll()
true true
} else {
false
}
}
) {
val rebaseInteractiveViewModel = tabViewModel.rebaseInteractiveViewModel
if (repositoryState == RepositoryState.REBASING_INTERACTIVE && rebaseInteractiveViewModel != null) {
RebaseInteractive(rebaseInteractiveViewModel)
} else { } else {
false Column(modifier = Modifier.weight(1f)) {
Menu(
modifier = Modifier
.padding(
top = 12.dp,
bottom = 16.dp
) // Linear progress bar already take 4 additional dp for top
.fillMaxWidth(),
menuViewModel = tabViewModel.menuViewModel,
onCreateBranch = { showNewBranchDialog = true },
onStashWithMessage = { showStashWithMessageDialog = true },
)
RepoContent(tabViewModel, diffSelected, selectedItem, repositoryState, blameState, showHistory)
}
} }
} }
) {
val rebaseInteractiveViewModel = tabViewModel.rebaseInteractiveViewModel
if (repositoryState == RepositoryState.REBASING_INTERACTIVE && rebaseInteractiveViewModel != null) {
RebaseInteractive(rebaseInteractiveViewModel)
} else {
Column(modifier = Modifier.weight(1f)) {
Menu(
modifier = Modifier
.padding(top = 4.dp, bottom = 8.dp) // Linear progress bar already take 4 additional dp for top
.fillMaxWidth(),
menuViewModel = tabViewModel.menuViewModel,
onRepositoryOpen = {
openRepositoryDialog(tabViewModel = tabViewModel)
},
onCreateBranch = { showNewBranchDialog = true },
onStashWithMessage = { showStashWithMessageDialog = true },
)
RepoContent(tabViewModel, diffSelected, selectedItem, repositoryState, blameState, showHistory)
}
} }
Spacer( Spacer(
@ -129,27 +150,142 @@ fun RepositoryOpenPage(tabViewModel: TabViewModel) {
.fillMaxWidth() .fillMaxWidth()
.background(MaterialTheme.colors.primaryVariant.copy(alpha = 0.2f)) .background(MaterialTheme.colors.primaryVariant.copy(alpha = 0.2f))
) )
Row(
BottomInfoBar(tabViewModel)
}
}
@Composable
fun SideBar(tabViewModel: TabViewModel) {
var showSettingsDialog by remember { mutableStateOf(false) }
if (showSettingsDialog) {
SettingsDialog(
settingsViewModel = tabViewModel.settingsViewModel,
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)
}
)
SideBarButton(
modifier = Modifier.padding(top = 16.dp),
painterName = "refresh.svg",
label = "Refresh repository information",
onClick = {
tabViewModel.refreshAll()
}
)
Spacer(modifier = Modifier.weight(1f))
SideBarButton(
painterName = "settings.svg",
label = "Settings",
onClick = { showSettingsDialog = true }
)
}
}
@Composable
private fun BottomInfoBar(tabViewModel: TabViewModel) {
val userInfo by tabViewModel.authorInfoSimple.collectAsState()
Row(
modifier = Modifier
.fillMaxWidth()
.height(32.dp)
.background(MaterialTheme.colors.surface)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxHeight()
.height(32.dp) .handMouseClickable { tabViewModel.showAuthorInfoDialog() },
.background(MaterialTheme.colors.surface) contentAlignment = Alignment.Center,
.padding(horizontal = 16.dp), ) {
verticalAlignment = Alignment.CenterVertically, Text(
text = "${userInfo.name ?: "Name not set"} <${userInfo.email ?: "Email not set"}>",
style = MaterialTheme.typography.body2,
)
}
}
}
@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( Box(
modifier = Modifier modifier = Modifier
.fillMaxHeight() .clip(RoundedCornerShape(4.dp))
.handMouseClickable { tabViewModel.showAuthorInfoDialog() }, .background(MaterialTheme.colors.background)
contentAlignment = Alignment.Center,
) { ) {
Text( Text(
text = "${userInfo.name ?: "Name not set"} <${userInfo.email ?: "Email not set"}>", text = label,
style = MaterialTheme.typography.body2, 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.primaryTextColor,
)
} }
} }
@ -246,6 +382,7 @@ fun MainContentView(
repositoryState = repositoryState, repositoryState = repositoryState,
) )
} }
else -> { else -> {
val diffViewModel = tabViewModel.diffViewModel val diffViewModel = tabViewModel.diffViewModel

View File

@ -29,6 +29,7 @@ import com.jetpackduba.gitnuro.AppStateManager
import com.jetpackduba.gitnuro.di.AppComponent import com.jetpackduba.gitnuro.di.AppComponent
import com.jetpackduba.gitnuro.di.DaggerTabComponent import com.jetpackduba.gitnuro.di.DaggerTabComponent
import com.jetpackduba.gitnuro.extensions.handMouseClickable import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.theme.primaryTextColor import com.jetpackduba.gitnuro.theme.primaryTextColor
import com.jetpackduba.gitnuro.viewmodels.TabViewModel import com.jetpackduba.gitnuro.viewmodels.TabViewModel
import javax.inject.Inject import javax.inject.Inject
@ -117,7 +118,7 @@ fun TabPanel(
onClick = onNewTabClicked, onClick = onNewTabClicked,
modifier = Modifier modifier = Modifier
.size(36.dp) .size(36.dp)
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
.align(Alignment.CenterStart), .align(Alignment.CenterStart),
) { ) {
Icon( Icon(

View File

@ -5,7 +5,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
fun repositoryAdditionalOptionsMenu( fun repositoryAdditionalOptionsMenu(
onOpenRepositoryOnFileExplorer: () -> Unit, onOpenRepositoryOnFileExplorer: () -> Unit,
onForceRepositoryRefresh: () -> Unit,
): List<DropDownContentData> { ): List<DropDownContentData> {
return mutableListOf( return mutableListOf(
DropDownContentData( DropDownContentData(
@ -13,10 +12,5 @@ fun repositoryAdditionalOptionsMenu(
icon = "source.svg", icon = "source.svg",
onClick = onOpenRepositoryOnFileExplorer, onClick = onOpenRepositoryOnFileExplorer,
), ),
DropDownContentData(
label = "Refresh repository",
icon = "refresh.svg",
onClick = onForceRepositoryRefresh,
),
) )
} }

View File

@ -16,13 +16,13 @@ import androidx.compose.ui.input.pointer.pointerHoverIcon
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 com.jetpackduba.gitnuro.extensions.handMouseClickable import com.jetpackduba.gitnuro.extensions.handMouseClickable
import com.jetpackduba.gitnuro.extensions.handOnHover
import com.jetpackduba.gitnuro.theme.* import com.jetpackduba.gitnuro.theme.*
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
import com.jetpackduba.gitnuro.ui.components.PrimaryButton import com.jetpackduba.gitnuro.ui.components.PrimaryButton
import com.jetpackduba.gitnuro.viewmodels.RemotesViewModel import com.jetpackduba.gitnuro.viewmodels.RemotesViewModel
import org.eclipse.jgit.transport.RemoteConfig import org.eclipse.jgit.transport.RemoteConfig
@OptIn(ExperimentalComposeUiApi::class)
@Composable @Composable
fun EditRemotesDialog( fun EditRemotesDialog(
remotesViewModel: RemotesViewModel, remotesViewModel: RemotesViewModel,
@ -90,7 +90,7 @@ fun EditRemotesDialog(
IconButton( IconButton(
onClick = onDismiss, onClick = onDismiss,
modifier = Modifier modifier = Modifier
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
) { ) {
Icon( Icon(
imageVector = Icons.Default.Clear, imageVector = Icons.Default.Clear,
@ -153,7 +153,7 @@ fun EditRemotesDialog(
IconButton( IconButton(
modifier = Modifier modifier = Modifier
.size(36.dp) .size(36.dp)
.pointerHoverIcon(PointerIconDefaults.Hand), .handOnHover(),
onClick = { onClick = {
val remotesWithNew = remotesEditorData.listRemotes.toMutableList() val remotesWithNew = remotesEditorData.listRemotes.toMutableList()
val newRemote = RemoteWrapper( val newRemote = RemoteWrapper(
@ -182,7 +182,7 @@ fun EditRemotesDialog(
IconButton( IconButton(
modifier = Modifier modifier = Modifier
.size(36.dp) .size(36.dp)
.pointerHoverIcon(PointerIconDefaults.Hand), .handOnHover(),
enabled = selectedRemote != null, enabled = selectedRemote != null,
onClick = { onClick = {
if (selectedRemote != null) if (selectedRemote != null)

View File

@ -550,7 +550,6 @@ fun HunkHeader(
} }
} }
@OptIn(ExperimentalComposeUiApi::class)
@Composable @Composable
private fun DiffHeader( private fun DiffHeader(
diffEntryType: DiffEntryType, diffEntryType: DiffEntryType,
@ -598,7 +597,7 @@ private fun DiffHeader(
IconButton( IconButton(
onClick = onCloseDiffView, onClick = onCloseDiffView,
modifier = Modifier modifier = Modifier
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
) { ) {
Image( Image(
painter = painterResource("close.svg"), painter = painterResource("close.svg"),
@ -677,7 +676,6 @@ fun UncommitedDiffFileHeaderButtons(
) )
} }
@OptIn(ExperimentalComposeUiApi::class)
@Composable @Composable
private fun PathOnlyDiffHeader( private fun PathOnlyDiffHeader(
filePath: String, filePath: String,
@ -703,7 +701,7 @@ private fun PathOnlyDiffHeader(
IconButton( IconButton(
onClick = onCloseDiffView, onClick = onCloseDiffView,
modifier = Modifier modifier = Modifier
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
) { ) {
Image( Image(
painter = painterResource("close.svg"), painter = painterResource("close.svg"),

View File

@ -333,7 +333,7 @@ fun SearchFilter(
IconButton( IconButton(
modifier = Modifier modifier = Modifier
.pointerHoverIcon(PointerIconDefaults.Hand), .handOnHover(),
onClick = { onClick = {
scope.launch { logViewModel.selectPreviousFilterCommit() } scope.launch { logViewModel.selectPreviousFilterCommit() }
} }
@ -343,7 +343,7 @@ fun SearchFilter(
IconButton( IconButton(
modifier = Modifier modifier = Modifier
.pointerHoverIcon(PointerIconDefaults.Hand), .handOnHover(),
onClick = { onClick = {
scope.launch { logViewModel.selectNextFilterCommit() } scope.launch { logViewModel.selectNextFilterCommit() }
} }
@ -353,7 +353,7 @@ fun SearchFilter(
IconButton( IconButton(
modifier = Modifier modifier = Modifier
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
.padding(end = 4.dp), .padding(end = 4.dp),
onClick = { logViewModel.closeSearch() } onClick = { logViewModel.closeSearch() }
) { ) {
@ -613,7 +613,7 @@ fun GraphHeader(
IconButton( IconButton(
modifier = Modifier modifier = Modifier
.padding(end = 8.dp) .padding(end = 8.dp)
.pointerHoverIcon(PointerIconDefaults.Hand), .handOnHover(),
onClick = onShowSearch onClick = onShowSearch
) { ) {
Icon( Icon(
@ -1135,7 +1135,7 @@ fun RefChip(
.clip(RoundedCornerShape(16.dp)) .clip(RoundedCornerShape(16.dp))
.border(width = 2.dp, color = color, shape = RoundedCornerShape(16.dp)) .border(width = 2.dp, color = color, shape = RoundedCornerShape(16.dp))
.combinedClickable(onDoubleClick = onCheckoutRef, onClick = {}) .combinedClickable(onDoubleClick = onCheckoutRef, onClick = {})
.pointerHoverIcon(PointerIconDefaults.Hand) .handOnHover()
) { ) {
ContextMenu( ContextMenu(
items = contextMenuItemsList items = contextMenuItemsList

View File

@ -68,10 +68,4 @@ class MenuViewModel @Inject constructor(
) { git -> ) { git ->
Desktop.getDesktop().open(git.repository.directory.parentFile) Desktop.getDesktop().open(git.repository.directory.parentFile)
} }
fun refresh() = tabState.safeProcessing(
refreshType = RefreshType.ALL_DATA,
) {
// Nothing to do here
}
} }

View File

@ -55,6 +55,7 @@ class TabViewModel @Inject constructor(
private val authorViewModelProvider: Provider<AuthorViewModel>, private val authorViewModelProvider: Provider<AuthorViewModel>,
private val tabState: TabState, private val tabState: TabState,
val appStateManager: AppStateManager, val appStateManager: AppStateManager,
val settingsViewModel: SettingsViewModel,
private val fileChangesWatcher: FileChangesWatcher, private val fileChangesWatcher: FileChangesWatcher,
private val updatesRepository: UpdatesRepository, private val updatesRepository: UpdatesRepository,
) { ) {

View File

@ -1,7 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>
fill="#000000">
<g>
<path d="M0,0h24v24H0V0z" fill="none"/>
<path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB