diff --git a/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/FloatingCopyButton.kt b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/FloatingCopyButton.kt new file mode 100644 index 00000000..8cc79a18 --- /dev/null +++ b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/FloatingCopyButton.kt @@ -0,0 +1,24 @@ +package com.sunnychung.application.multiplatform.hellohttp.ux + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.sunnychung.application.multiplatform.hellohttp.ux.local.LocalColor + +@Composable +fun FloatingCopyButton(size: Dp = 20.dp, innerPadding: Dp = 4.dp, modifier: Modifier = Modifier, onClick: () -> Unit) { + val colours = LocalColor.current + AppImageButton( + resource = "copy-to-clipboard.svg", + size = size + innerPadding * 2, + innerPadding = PaddingValues(innerPadding), + color = colours.copyButton, + onClick = onClick, + modifier = modifier + .background(colours.backgroundFloatingButton, RoundedCornerShape(4.dp)) + ) +} diff --git a/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/KeyValueTableView.kt b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/KeyValueTableView.kt index 3dd39278..495c4d9a 100644 --- a/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/KeyValueTableView.kt +++ b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/KeyValueTableView.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.unit.dp import com.sunnychung.application.multiplatform.hellohttp.ux.local.LocalColor @Composable -fun KeyValueTableView(modifier: Modifier = Modifier, keyValues: List>) { +fun KeyValueTableView(modifier: Modifier = Modifier, keyValues: List>, isCopyable: Boolean = false) { val colors = LocalColor.current Column(modifier) { @@ -30,8 +30,42 @@ fun KeyValueTableView(modifier: Modifier = Modifier, keyValues: List ResponseStreamView(response) ResponseTab.Header -> if (response.headers != null) { - KeyValueTableView(keyValues = response.headers!!, modifier = Modifier.fillMaxSize().padding(8.dp)) + KeyValueTableView(keyValues = response.headers!!, isCopyable = true, modifier = Modifier.fillMaxSize().padding(8.dp)) } else { ResponseEmptyView(type = "header", isCommunicating = connectionStatus.isConnectionActive(), modifier = Modifier.fillMaxSize().padding(8.dp)) } @@ -507,25 +514,28 @@ fun BodyViewerView( PrettifyResult(contentToUse.decodeToString() ?: "") } - CodeEditorView( - isReadOnly = true, - text = prettifyResult.prettyString, - collapsableLines = prettifyResult.collapsableLineRange, - collapsableChars = prettifyResult.collapsableCharRange, - transformations = if (selectedView.prettifier!!.formatName.contains("JSON")) { - listOf(JsonSyntaxHighlightTransformation(colours = colours)) - } else { - emptyList() - }, - modifier = modifier, - ) + CopyableContentContainer(textToCopy = prettifyResult.prettyString, modifier = modifier) { + CodeEditorView( + isReadOnly = true, + text = prettifyResult.prettyString, + collapsableLines = prettifyResult.collapsableLineRange, + collapsableChars = prettifyResult.collapsableCharRange, + transformations = if (selectedView.prettifier!!.formatName.contains("JSON")) { + listOf(JsonSyntaxHighlightTransformation(colours = colours)) + } else { + emptyList() + }, + ) + } } else { - CodeEditorView( - isReadOnly = true, - text = errorMessage ?: content.decodeToString(), - textColor = colours.warning, - modifier = modifier, - ) + val text = errorMessage ?: content.decodeToString() + CopyableContentContainer(textToCopy = text, modifier = modifier) { + CodeEditorView( + isReadOnly = true, + text = text, + textColor = colours.warning, + ) + } } if (isEnableJsonPath) { AppTextFieldWithPlaceholder( @@ -591,6 +601,51 @@ fun ResponseBodyView(response: UserResponse) { } } +@Composable +@OptIn(ExperimentalComposeUiApi::class) +fun CopyableContentContainer( + modifier: Modifier = Modifier, + textToCopy: String, + isEnabled: Boolean = true, + size: Dp = 20.dp, + innerPadding: Dp = 4.dp, + outerPadding: PaddingValues = PaddingValues(top = 4.dp, end = 12.dp), + contentView: @Composable () -> Unit +) { + if (!isEnabled) { + Box(modifier = modifier) { + contentView() + } + return + } + + val clipboardManager = LocalClipboardManager.current + var isShowCopyButton by remember { mutableStateOf(false) } + + Box( + modifier = modifier + .onPointerEvent(PointerEventType.Enter) { + isShowCopyButton = true + } + .onPointerEvent(PointerEventType.Exit) { + isShowCopyButton = false + } + ) { + contentView() + if (isShowCopyButton) { + FloatingCopyButton( + size = size, + innerPadding = innerPadding, + modifier = Modifier + .align(Alignment.TopEnd) + .padding(outerPadding) + ) { + clipboardManager.setText(AnnotatedString(textToCopy)) + } + } + } +} + private val DATE_TIME_FORMAT = KDateTimeFormat("HH:mm:ss.lll") private val TIMESTAMP_COLUMN_WIDTH_DP = 120.dp private val TYPE_COLUMN_WIDTH_DP = 20.dp diff --git a/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/local/AppColor.kt b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/local/AppColor.kt index 1dd34b4e..ef9ccda0 100644 --- a/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/local/AppColor.kt +++ b/src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/local/AppColor.kt @@ -17,6 +17,7 @@ data class AppColor( val backgroundInputFieldHighlight: Color, val backgroundInputFieldHighlightEmphasize: Color, val backgroundCollapsed: Color, + val backgroundFloatingButton: Color = background.copy(alpha = 0.7f), val primary: Color, val bright: Color, @@ -58,6 +59,8 @@ data class AppColor( val scrollBarUnhover: Color, val scrollBarHover: Color, + val copyButton: Color = image.copy(alpha = 0.8f), + val syntaxColor: SyntaxColor, ) diff --git a/src/jvmMain/resources/image/copy-to-clipboard.svg b/src/jvmMain/resources/image/copy-to-clipboard.svg new file mode 100644 index 00000000..219ad11b --- /dev/null +++ b/src/jvmMain/resources/image/copy-to-clipboard.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file