diff --git a/src/main/kotlin/app/ui/Branches.kt b/src/main/kotlin/app/ui/Branches.kt index 0b670f4..433ed0f 100644 --- a/src/main/kotlin/app/ui/Branches.kt +++ b/src/main/kotlin/app/ui/Branches.kt @@ -2,12 +2,7 @@ package app.ui import androidx.compose.foundation.ContextMenuArea import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.runtime.* @@ -16,9 +11,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import app.extensions.isLocal import app.extensions.simpleName -import app.maxSidePanelHeight -import app.ui.components.ScrollableLazyColumn -import app.ui.components.SideMenuEntry +import app.ui.components.SideMenuPanel import app.ui.components.SideMenuSubentry import app.ui.context_menu.branchContextMenuItems import app.ui.dialogs.MergeDialog @@ -35,29 +28,21 @@ fun Branches( val currentBranch by branchesViewModel.currentBranch.collectAsState() val (mergeBranch, setMergeBranch) = remember { mutableStateOf(null) } val (rebaseBranch, setRebaseBranch) = remember { mutableStateOf(null) } - val maxHeight = remember(branches) { maxSidePanelHeight(branches.count()) } - Column { - SideMenuEntry("Local branches") - - ScrollableLazyColumn( - modifier = Modifier - .fillMaxWidth() - .heightIn(max = maxHeight.dp) - .background(MaterialTheme.colors.background) - ) { - itemsIndexed(branches) { _, branch -> - BranchLineEntry( - branch = branch, - isCurrentBranch = currentBranch == branch.name, - onBranchClicked = { onBranchClicked(branch) }, - onCheckoutBranch = { branchesViewModel.checkoutRef(branch) }, - onMergeBranch = { setMergeBranch(branch) }, - onDeleteBranch = { branchesViewModel.deleteBranch(branch) }, - onRebaseBranch = { setRebaseBranch(branch) }, - ) - } - } + SideMenuPanel( + title = "Local branches", + icon = painterResource("branch.svg"), + items = branches + ) { branch -> + BranchLineEntry( + branch = branch, + isCurrentBranch = currentBranch == branch.name, + onBranchClicked = { onBranchClicked(branch) }, + onCheckoutBranch = { branchesViewModel.checkoutRef(branch) }, + onMergeBranch = { setMergeBranch(branch) }, + onDeleteBranch = { branchesViewModel.deleteBranch(branch) }, + onRebaseBranch = { setRebaseBranch(branch) }, + ) } if (mergeBranch != null) { diff --git a/src/main/kotlin/app/ui/Remotes.kt b/src/main/kotlin/app/ui/Remotes.kt index 2b6c0b5..eb99b0c 100644 --- a/src/main/kotlin/app/ui/Remotes.kt +++ b/src/main/kotlin/app/ui/Remotes.kt @@ -1,48 +1,36 @@ package app.ui -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.lazy.items -import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import app.extensions.simpleVisibleName import app.git.RemoteInfo -import app.maxSidePanelHeight -import app.ui.components.ScrollableLazyColumn -import app.ui.components.SideMenuEntry +import app.ui.components.SideMenuPanel import app.ui.components.SideMenuSubentry import app.viewmodels.RemotesViewModel @Composable fun Remotes(remotesViewModel: RemotesViewModel) { val remotes by remotesViewModel.remotes.collectAsState() - val allBranches = remotes.map { it.branchesList }.flatten() - val maxHeight = remember(remotes) { maxSidePanelHeight(allBranches.count() + remotes.count()) } - Column { - SideMenuEntry("Remotes") - - ScrollableLazyColumn( - modifier = Modifier - .fillMaxWidth() - .heightIn(max = maxHeight.dp) - .background(MaterialTheme.colors.background) - ) { - items(remotes) { remote -> - RemoteRow( - remote = remote, - ) - } - } + val itemsCount = remember(remotes) { + val allBranches = remotes.map { it.branchesList }.flatten() + allBranches.count() + remotes.count() } + + SideMenuPanel( + title = "Remotes", + icon = painterResource("cloud.svg"), + items = remotes, + itemsCountForMaxHeight = itemsCount, + itemContent = { remoteInfo -> + RemoteRow(remoteInfo) + } + ) } @Composable diff --git a/src/main/kotlin/app/ui/Stashes.kt b/src/main/kotlin/app/ui/Stashes.kt index da5b06b..64d42aa 100644 --- a/src/main/kotlin/app/ui/Stashes.kt +++ b/src/main/kotlin/app/ui/Stashes.kt @@ -10,10 +10,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import app.maxSidePanelHeight import app.ui.components.ScrollableLazyColumn import app.ui.components.SideMenuEntry +import app.ui.components.SideMenuPanel import app.ui.components.SideMenuSubentry import app.viewmodels.StashStatus import app.viewmodels.StashesViewModel @@ -32,29 +34,18 @@ fun Stashes( else listOf() - val maxHeight = remember(stashList) { maxSidePanelHeight(stashList.count()) } - Column { - SideMenuEntry( - text = "Stashes", - ) - - ScrollableLazyColumn( - modifier = Modifier - .fillMaxWidth() - .heightIn(max = maxHeight.dp) - .background(MaterialTheme.colors.background) - ) { - items(items = stashList) { stash -> - StashRow( - stash = stash, - onClick = { - onStashSelected(stash) - } - ) - } + SideMenuPanel( + title = "Stashes", + icon = painterResource("stash.svg"), + items = stashList, + itemContent = { stashInfo -> + StashRow( + stash = stashInfo, + onClick = { onStashSelected(stashInfo) } + ) } - } + ) } diff --git a/src/main/kotlin/app/ui/Tags.kt b/src/main/kotlin/app/ui/Tags.kt index aac0b13..24e424f 100644 --- a/src/main/kotlin/app/ui/Tags.kt +++ b/src/main/kotlin/app/ui/Tags.kt @@ -12,11 +12,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import app.extensions.simpleName import app.maxSidePanelHeight import app.ui.components.ScrollableLazyColumn import app.ui.components.SideMenuEntry +import app.ui.components.SideMenuPanel import app.ui.components.SideMenuSubentry import app.ui.context_menu.tagContextMenuItems import app.viewmodels.TagsViewModel @@ -29,31 +31,20 @@ fun Tags( ) { val tagsState = tagsViewModel.tags.collectAsState() val tags = tagsState.value - val maxHeight = remember(tags) { maxSidePanelHeight(tags.count()) } - Column { - SideMenuEntry( - text = "Tags", - ) - - ScrollableLazyColumn( - modifier = Modifier - .fillMaxWidth() - .heightIn(max = maxHeight.dp) - .background(MaterialTheme.colors.background) - ) { - items(items = tags) { tag -> - TagRow( - tag = tag, - onTagClicked = { onTagClicked(tag) }, - onCheckoutTag = { tagsViewModel.checkoutRef(tag) }, - onDeleteTag = { tagsViewModel.deleteTag(tag) } - ) - } + SideMenuPanel( + title = "Tags", + items = tags, + icon = painterResource("tag.svg"), + itemContent = { tag -> + TagRow( + tag = tag, + onTagClicked = { onTagClicked(tag) }, + onCheckoutTag = { tagsViewModel.checkoutRef(tag) }, + onDeleteTag = { tagsViewModel.deleteTag(tag) } + ) } - - } - + ) } @OptIn(ExperimentalFoundationApi::class) diff --git a/src/main/kotlin/app/ui/components/Expandable.kt b/src/main/kotlin/app/ui/components/Expandable.kt new file mode 100644 index 0000000..35bb60c --- /dev/null +++ b/src/main/kotlin/app/ui/components/Expandable.kt @@ -0,0 +1,35 @@ +package app.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier + +@OptIn(ExperimentalAnimationApi::class) +@Composable +fun VerticalExpandable( + header: @Composable () -> Unit, + child: @Composable () -> Unit, +) { + var isExpanded by remember { + mutableStateOf(true) + } + Column { + Box( + modifier = Modifier.clickable { + isExpanded = !isExpanded + } + ) { + header() + } + + AnimatedVisibility(visible = isExpanded) { + child() + } + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/app/ui/components/SideMenuEntry.kt b/src/main/kotlin/app/ui/components/SideMenuEntry.kt index 69854a5..4671c8f 100644 --- a/src/main/kotlin/app/ui/components/SideMenuEntry.kt +++ b/src/main/kotlin/app/ui/components/SideMenuEntry.kt @@ -1,15 +1,15 @@ package app.ui.components import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* +import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -20,6 +20,8 @@ import app.theme.secondaryTextColor @Composable fun SideMenuEntry( text: String, + icon: Painter? = null, + itemsCount: Int, ) { Row( modifier = Modifier @@ -28,14 +30,34 @@ fun SideMenuEntry( .background(color = MaterialTheme.colors.headerBackground), verticalAlignment = Alignment.CenterVertically, ) { + if(icon != null) { + Icon( + painter = icon, + contentDescription = null, + tint = MaterialTheme.colors.primaryTextColor, + modifier = Modifier + .padding(start = 8.dp) + .size(16.dp), + ) + } + Text( text = text, modifier = Modifier - .padding(horizontal = 8.dp), + .padding(horizontal = 8.dp) + .weight(1f), maxLines = 1, fontSize = 14.sp, color = MaterialTheme.colors.primaryTextColor, overflow = TextOverflow.Ellipsis, ) + + + Text( + text = itemsCount.toString(), + fontSize = 14.sp, + color = MaterialTheme.colors.secondaryTextColor, + modifier = Modifier.padding(end = 8.dp), + ) } } \ No newline at end of file diff --git a/src/main/kotlin/app/ui/components/SideMenuPanel.kt b/src/main/kotlin/app/ui/components/SideMenuPanel.kt new file mode 100644 index 0000000..537d775 --- /dev/null +++ b/src/main/kotlin/app/ui/components/SideMenuPanel.kt @@ -0,0 +1,48 @@ +package app.ui.components + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.unit.dp +import app.maxSidePanelHeight + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun SideMenuPanel( + title: String, + icon: Painter? = null, + items: List, + itemsCountForMaxHeight: Int = items.count(), + itemContent: @Composable (T) -> Unit, +) { + val maxHeight = remember(items) { maxSidePanelHeight(itemsCountForMaxHeight) } + + VerticalExpandable( + header = { + SideMenuEntry( + text = title, + icon = icon, + itemsCount = items.count() + ) + }, + ) { + ScrollableLazyColumn( + modifier = Modifier + .fillMaxWidth() + .heightIn(max = maxHeight.dp) + .background(MaterialTheme.colors.background) + ) { + items(items) { item -> + itemContent(item) + } + } + } +} \ No newline at end of file