Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions designsystem/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
<ID>CyclomaticComplexMethod:DiffLine.kt$@Composable fun AslDiffLine( kind: AslDiffKind, text: String, modifier: Modifier = Modifier, oldNo: Int? = null, newNo: Int? = null, noWrap: Boolean = false, )</ID>
<ID>CyclomaticComplexMethod:FileIcons.kt$AslFileIcons$fun iconFor(fileName: String): String</ID>
<ID>CyclomaticComplexMethod:FileIcons.kt$AslFileIcons$fun tintFor(fileName: String, colors: AslColorScheme): Color</ID>
<ID>CyclomaticComplexMethod:FileTree.kt$@Composable private fun AslFileTreeRow( node: AslFileTreeNode, depth: Int, expanded: Boolean, selected: Boolean, rowWidth: Dp, actionsEnabled: Boolean, canPaste: Boolean, selectDirectories: Boolean, onToggle: (String) -&gt; Unit, onSelect: (AslFileTreeNode) -&gt; Unit, onFocus: (AslFileTreeNode) -&gt; Unit, onAction: (AslFileTreeNode, AslFileTreeAction) -&gt; Unit, )</ID>
<ID>CyclomaticComplexMethod:ListItem.kt$@Composable fun AslListItem( title: String, modifier: Modifier = Modifier, subtitle: String? = null, icon: String? = null, iconColor: Color? = null, leading: (@Composable () -&gt; Unit)? = null, trailing: (@Composable () -&gt; Unit)? = null, divider: Boolean = true, selected: Boolean = false, disabled: Boolean = false, onClick: (() -&gt; Unit)? = null, )</ID>
<ID>CyclomaticComplexMethod:TextField.kt$@OptIn(ExperimentalFoundationApi::class) @Composable fun AslTextField( value: String, onValueChange: (String) -&gt; Unit, modifier: Modifier = Modifier, label: String? = null, placeholder: String? = null, helper: String? = null, error: String? = null, disabled: Boolean = false, type: AslTextFieldType = AslTextFieldType.Text, leadingIcon: String? = null, trailingIcon: String? = null, onTrailingClick: (() -&gt; Unit)? = null, onFocusChanged: ((Boolean) -&gt; Unit)? = null, )</ID>
<ID>CyclomaticComplexMethod:ToolCallCard.kt$private fun computeLineDiff(old: String?, new: String): List&lt;DiffLine&gt;</ID>
Expand Down Expand Up @@ -82,7 +81,6 @@
<ID>LongParameterList:Button.kt$( label: String, onClick: () -&gt; Unit, modifier: Modifier = Modifier, variant: AslButtonVariant = AslButtonVariant.Primary, size: AslButtonSize = AslButtonSize.Md, icon: String? = null, trailingIcon: String? = null, disabled: Boolean = false, loading: Boolean = false, fullWidth: Boolean = false, )</ID>
<ID>LongParameterList:Chip.kt$( label: String, modifier: Modifier = Modifier, kind: AslChipKind = AslChipKind.Filter, selected: Boolean = false, disabled: Boolean = false, icon: String? = null, status: AslChipStatus = AslChipStatus.Neutral, onRemove: (() -&gt; Unit)? = null, onClick: (() -&gt; Unit)? = null, )</ID>
<ID>LongParameterList:Dialog.kt$( title: String, onDismiss: () -&gt; Unit, modifier: Modifier = Modifier, variant: AslDialogVariant = AslDialogVariant.Alert, body: String? = null, confirmLabel: String = stringResource(R.string.asl_action_ok), cancelLabel: String? = null, destructive: Boolean = false, onConfirm: () -&gt; Unit = {}, inputContent: (@Composable () -&gt; Unit)? = null, )</ID>
<ID>LongParameterList:EditorToolbar.kt$( modifier: Modifier = Modifier, projectName: String = "MyApplication", running: Boolean = false, onRun: () -&gt; Unit = {}, onMenu: () -&gt; Unit = {}, actions: @Composable (RowScope.() -&gt; Unit)? = null, overflowItems: List&lt;AslOverflowMenuEntry&gt; = emptyList(), onOverflowSelect: (AslOverflowMenuEntry.Item, Int) -&gt; Unit = { _, _ -&gt; }, )</ID>
<ID>LongParameterList:EmptyState.kt$( title: String, modifier: Modifier = Modifier, icon: String = "folder-open", subtitle: String? = null, actionLabel: String? = null, onAction: () -&gt; Unit = {}, secondaryLabel: String? = null, onSecondary: () -&gt; Unit = {}, )</ID>
<ID>LongParameterList:ErrorState.kt$( title: String, modifier: Modifier = Modifier, icon: String = "triangle-alert", explanation: String? = null, detail: String? = null, actionLabel: String = stringResource(R.string.asl_action_retry), onAction: () -&gt; Unit = {}, secondaryLabel: String? = null, onSecondary: () -&gt; Unit = {}, )</ID>
<ID>LongParameterList:FileTree.kt$( items: List&lt;AslFileTreeNode&gt;, expandedIds: Set&lt;String&gt;, modifier: Modifier = Modifier, selectedId: String? = null, actionsEnabled: Boolean = false, canPaste: Boolean = false, selectDirectories: Boolean = false, onToggle: (String) -&gt; Unit = {}, onSelect: (AslFileTreeNode) -&gt; Unit = {}, onFocus: (AslFileTreeNode) -&gt; Unit = {}, onAction: (AslFileTreeNode, AslFileTreeAction) -&gt; Unit = { _, _ -&gt; }, )</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
Expand Down Expand Up @@ -191,111 +193,176 @@ private fun AslFileTreeRow(
},
),
) {
Row(
FileTreeRowContent(
node = node,
expanded = expanded,
selected = selected,
selectDirectories = selectDirectories,
callbacks = FileTreeRowCallbacks(onToggle, onSelect, onFocus),
modifier = Modifier
.width(rowWidth)
.height(AslMetrics.treeRow)
.padding(start = (8 + depth * 16).dp, end = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
if (isDir) {
val rotation by animateFloatAsState(
targetValue = if (expanded) 90f else 0f,
animationSpec = AslMotion.standardSpec(),
label = "chevronRotation",
)
Box(
modifier = Modifier
.size(20.dp)
.then(
if (selectDirectories) {
Modifier.pointerInput(node.id) {
detectTapGestures(onTap = { onToggle(node.id) })
}
} else {
Modifier
},
),
contentAlignment = Alignment.Center,
) {
AslIcon(
name = "chevron-right",
size = 14.dp,
tint = colors.textTertiary,
modifier = Modifier.rotate(rotation),
)
}
} else {
Box(modifier = Modifier.width(14.dp))
}
Spacer(Modifier.width(6.dp))
Row(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.then(
if (selectDirectories) {
Modifier.pointerInput(node.id) {
detectTapGestures(onTap = {
onFocus(node)
onSelect(node)
})
}
} else {
Modifier
},
),
verticalAlignment = Alignment.CenterVertically,
) {
AslIcon(
name = node.icon ?: if (isDir) (if (expanded) "folder-open" else "folder") else "file",
size = 16.dp,
tint = when {
isDir -> colors.textSecondary
else -> AslFileIcons.tintFor(node.name, colors)
},
)
Spacer(Modifier.width(6.dp))
Text(
text = node.name,
style = MaterialTheme.typography.bodySmall,
fontWeight = if (selected) FontWeight.Medium else FontWeight.Normal,
color = node.git?.let { gitTint(it, colors) } ?: colors.textPrimary,
maxLines = 1,
softWrap = false,
)
if (node.git != null) {
Spacer(Modifier.width(8.dp))
Text(
text = node.git.letter,
style = AslCode.codeTiny,
color = gitTint(node.git, colors),
)
}
)
if (actionsEnabled && menuOpen) {
FileTreeRowMenu(menuAnchor, isDir, canPaste, openMenuUpward, onDismiss = { menuOpen = false }) { action ->
menuOpen = false
onAction(node, action)
}
}
if (actionsEnabled && menuOpen) {
Box(
modifier = Modifier
.align(Alignment.TopStart)
.offset(x = menuAnchor.x, y = menuAnchor.y)
.size(1.dp),
) {
AslFileTreeActionMenu(
isDirectory = isDir,
canPaste = canPaste,
openUpward = openMenuUpward,
onDismiss = { menuOpen = false },
onSelect = { action ->
menuOpen = false
onAction(node, action)
}
}

@Immutable
private data class FileTreeRowCallbacks(
val onToggle: (String) -> Unit,
val onSelect: (AslFileTreeNode) -> Unit,
val onFocus: (AslFileTreeNode) -> Unit,
)

@Composable
private fun FileTreeRowContent(
node: AslFileTreeNode,
expanded: Boolean,
selected: Boolean,
selectDirectories: Boolean,
callbacks: FileTreeRowCallbacks,
modifier: Modifier = Modifier,
) {
val isDirectory = node.children != null
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
) {
FileTreeRowChevron(node, isDirectory, expanded, selectDirectories, callbacks.onToggle)
Spacer(Modifier.width(6.dp))
FileTreeRowLabel(node, isDirectory, expanded, selected, selectDirectories, callbacks.onSelect, callbacks.onFocus)
}
}

@Composable
private fun FileTreeRowChevron(
node: AslFileTreeNode,
isDirectory: Boolean,
expanded: Boolean,
selectDirectories: Boolean,
onToggle: (String) -> Unit,
) {
if (isDirectory) {
val colors = AslTheme.colors
val rotation by animateFloatAsState(
targetValue = if (expanded) 90f else 0f,
animationSpec = AslMotion.standardSpec(),
label = "chevronRotation",
)
Box(
modifier = Modifier
.size(20.dp)
.then(
if (selectDirectories) {
Modifier.pointerInput(node.id) {
detectTapGestures(onTap = { onToggle(node.id) })
}
} else {
Modifier
},
)
}
),
contentAlignment = Alignment.Center,
) {
AslIcon(
name = "chevron-right",
size = 14.dp,
tint = colors.textTertiary,
modifier = Modifier.rotate(rotation),
)
}
} else {
Box(modifier = Modifier.width(14.dp))
}
}

@Composable
private fun RowScope.FileTreeRowLabel(
node: AslFileTreeNode,
isDirectory: Boolean,
expanded: Boolean,
selected: Boolean,
selectDirectories: Boolean,
onSelect: (AslFileTreeNode) -> Unit,
onFocus: (AslFileTreeNode) -> Unit,
) {
val colors = AslTheme.colors
Row(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.then(
if (selectDirectories) {
Modifier.pointerInput(node.id) {
detectTapGestures(onTap = {
onFocus(node)
onSelect(node)
})
}
} else {
Modifier
},
),
verticalAlignment = Alignment.CenterVertically,
) {
AslIcon(
name = node.icon ?: if (isDirectory) (if (expanded) "folder-open" else "folder") else "file",
size = 16.dp,
tint = when {
isDirectory -> colors.textSecondary
else -> AslFileIcons.tintFor(node.name, colors)
},
)
Spacer(Modifier.width(6.dp))
Text(
text = node.name,
style = MaterialTheme.typography.bodySmall,
fontWeight = if (selected) FontWeight.Medium else FontWeight.Normal,
color = node.git?.let { gitTint(it, colors) } ?: colors.textPrimary,
maxLines = 1,
softWrap = false,
)
if (node.git != null) {
Spacer(Modifier.width(8.dp))
Text(
text = node.git.letter,
style = AslCode.codeTiny,
color = gitTint(node.git, colors),
)
}
}
}

@Composable
private fun BoxScope.FileTreeRowMenu(
menuAnchor: DpOffset,
isDirectory: Boolean,
canPaste: Boolean,
openUpward: Boolean,
onDismiss: () -> Unit,
onSelect: (AslFileTreeAction) -> Unit,
) {
Box(
modifier = Modifier
.align(Alignment.TopStart)
.offset(x = menuAnchor.x, y = menuAnchor.y)
.size(1.dp),
) {
AslFileTreeActionMenu(
isDirectory = isDirectory,
canPaste = canPaste,
openUpward = openUpward,
onDismiss = onDismiss,
onSelect = onSelect,
)
}
}

@Composable
private fun AslFileTreeActionMenu(
isDirectory: Boolean,
Expand Down
4 changes: 0 additions & 4 deletions feature/git/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,21 @@
<ID>CyclomaticComplexMethod:GitAuthPrompt.kt$@Composable fun GitHubAuthDialog(state: GitAuthPromptState, actions: GitAuthPromptActions)</ID>
<ID>CyclomaticComplexMethod:GitErrorMessageMapper.kt$fun gitErrorMessage(error: Throwable): String</ID>
<ID>CyclomaticComplexMethod:GitHistoryScreen.kt$@Composable private fun GitHistoryScreen( uiState: GitHistoryUiState, interactionListener: GitHistoryInteractionListener, onBack: () -&gt; Unit, onOpenDiff: (String, String) -&gt; Unit, )</ID>
<ID>CyclomaticComplexMethod:GitPanelScreen.kt$@Composable private fun GitChangesHeader( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, )</ID>
<ID>CyclomaticComplexMethod:GitPanelScreen.kt$@Composable private fun GitPanelScreen( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, onClose: () -&gt; Unit, onOpenDiff: (String, GitDiffTarget) -&gt; Unit, onOpenHistory: () -&gt; Unit, onOpenBranches: () -&gt; Unit, onOpenTags: () -&gt; Unit, onOpenStashes: () -&gt; Unit, onOpenConflicts: () -&gt; Unit, )</ID>
<ID>ForbiddenImport:GitDiffScreen.kt$import androidx.compose.material3.MaterialTheme</ID>
<ID>ForbiddenImport:GitHistoryScreen.kt$import androidx.compose.material3.MaterialTheme</ID>
<ID>LambdaParameterInRestartableEffect:GitHistoryScreen.kt$onLoadNext</ID>
<ID>LongMethod:GitAuthPrompt.kt$@Composable fun GitHubAuthDialog(state: GitAuthPromptState, actions: GitAuthPromptActions)</ID>
<ID>LongMethod:GitDiffScreen.kt$@Composable private fun GitDiffScreen( uiState: GitDiffUiState, interactionListener: GitDiffInteractionListener, onBack: () -&gt; Unit, )</ID>
<ID>LongMethod:GitHistoryScreen.kt$@Composable private fun GitHistoryScreen( uiState: GitHistoryUiState, interactionListener: GitHistoryInteractionListener, onBack: () -&gt; Unit, onOpenDiff: (String, String) -&gt; Unit, )</ID>
<ID>LongMethod:GitPanelScreen.kt$@Composable private fun GitChangesHeader( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, )</ID>
<ID>LongMethod:GitPanelScreen.kt$@Composable private fun GitPanelScreen( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, onClose: () -&gt; Unit, onOpenDiff: (String, GitDiffTarget) -&gt; Unit, onOpenHistory: () -&gt; Unit, onOpenBranches: () -&gt; Unit, onOpenTags: () -&gt; Unit, onOpenStashes: () -&gt; Unit, onOpenConflicts: () -&gt; Unit, )</ID>
<ID>LongMethod:GitPanelScreen.kt$@Composable private fun RemotesView(uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener)</ID>
<ID>LongMethod:GitRefsScreen.kt$@Composable private fun BranchList( state: GitRefsUiState, interactionListener: GitRefsInteractionListener, onRename: (GitBranch) -&gt; Unit, onDelete: (GitBranch) -&gt; Unit, onMerge: (GitBranch) -&gt; Unit, )</ID>
<ID>LongMethod:GitRefsScreen.kt$@Composable private fun GitRefsScreen( uiState: GitRefsUiState, interactionListener: GitRefsInteractionListener, onBack: () -&gt; Unit, )</ID>
<ID>LongParameterList:GitPanelApi.kt$GitPanelApi$( projectId: String, onClose: () -&gt; Unit, onOpenDiff: (String, GitDiffTarget) -&gt; Unit = { _, _ -&gt; }, onOpenHistory: () -&gt; Unit = {}, onOpenBranches: () -&gt; Unit = {}, onOpenTags: () -&gt; Unit = {}, onOpenStashes: () -&gt; Unit = {}, onOpenConflicts: () -&gt; Unit = {}, )</ID>
<ID>LongParameterList:GitPanelScreen.kt$( projectId: String, onClose: () -&gt; Unit, onOpenDiff: (String, GitDiffTarget) -&gt; Unit = { _, _ -&gt; }, onOpenHistory: () -&gt; Unit = {}, onOpenBranches: () -&gt; Unit = {}, onOpenTags: () -&gt; Unit = {}, onOpenStashes: () -&gt; Unit = {}, onOpenConflicts: () -&gt; Unit = {}, viewModel: GitPanelViewModel = koinViewModel { parametersOf(projectId) }, )</ID>
<ID>LongParameterList:GitPanelScreen.kt$( uiState: GitPanelUiState, interactionListener: GitPanelInteractionListener, onClose: () -&gt; Unit, onOpenDiff: (String, GitDiffTarget) -&gt; Unit, onOpenHistory: () -&gt; Unit, onOpenBranches: () -&gt; Unit, onOpenTags: () -&gt; Unit, onOpenStashes: () -&gt; Unit, onOpenConflicts: () -&gt; Unit, )</ID>
<ID>ModifierMissing:GitAuthPrompt.kt$GitHubAuthDialog</ID>
<ID>ModifierMissing:GitBlameScreen.kt$GitBlameRoute</ID>
<ID>MultipleEmitters:GitPanelScreen.kt$GitChangesHeader</ID>
<ID>TooGenericExceptionCaught:GitPanelControllerContext.kt$GitPanelControllerContext$error: Throwable</ID>
<ID>TooGenericExceptionCaught:GitPanelViewModel.kt$GitPanelViewModel$pushError: Throwable</ID>
</CurrentIssues>
Expand Down
Loading
Loading