diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/TextItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/TextItemView.kt index af7d32d52d..64855e3195 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/TextItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/TextItemView.kt @@ -1,26 +1,28 @@ package chat.simplex.common.views.chat.item -import androidx.compose.foundation.text.* +import androidx.compose.foundation.text.BasicText +import androidx.compose.foundation.text.InlineTextContent import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.input.pointer.* import androidx.compose.ui.platform.* import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.* +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.sp import chat.simplex.common.model.* -import chat.simplex.common.platform.Log -import chat.simplex.common.platform.TAG +import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.CurrentColors import chat.simplex.common.views.helpers.* import kotlinx.coroutines.* +import java.awt.* val reserveTimestampStyle = SpanStyle(color = Color.Transparent) val boldFont = SpanStyle(fontWeight = FontWeight.Medium) @@ -156,7 +158,8 @@ fun MarkdownText ( else */if (meta != null) withStyle(reserveTimestampStyle) { append(reserve) } } if (hasLinks && uriHandler != null) { - ClickableText(annotatedText, style = style, modifier = modifier, maxLines = maxLines, overflow = overflow, + val icon = remember { mutableStateOf(PointerIcon.Default) } + ClickableText(annotatedText, style = style, modifier = modifier.pointerHoverIcon(icon.value), maxLines = maxLines, overflow = overflow, onLongClick = { offset -> annotatedText.getStringAnnotations(tag = "URL", start = offset, end = offset) .firstOrNull()?.let { annotation -> onLinkLongClick(annotation.item) } @@ -179,6 +182,15 @@ fun MarkdownText ( uriHandler.openVerifiedSimplexUri(annotation.item) } }, + onHover = { offset -> + icon.value = annotatedText.getStringAnnotations(tag = "URL", start = offset, end = offset) + .firstOrNull()?.let { + PointerIcon.Hand + } ?: annotatedText.getStringAnnotations(tag = "SIMPLEX_URL", start = offset, end = offset) + .firstOrNull()?.let { + PointerIcon.Hand + } ?: PointerIcon.Default + }, shouldConsumeEvent = { offset -> annotatedText.getStringAnnotations(tag = "URL", start = offset, end = offset).any() annotatedText.getStringAnnotations(tag = "SIMPLEX_URL", start = offset, end = offset).any() @@ -202,6 +214,7 @@ fun ClickableText( onTextLayout: (TextLayoutResult) -> Unit = {}, onClick: (Int) -> Unit, onLongClick: (Int) -> Unit = {}, + onHover: (Int) -> Unit = {}, shouldConsumeEvent: (Int) -> Boolean ) { val layoutResult = remember { mutableStateOf(null) } @@ -225,6 +238,14 @@ fun ClickableText( consume } ) + }.pointerInput(onHover) { + if (appPlatform.isDesktop) { + detectCursorMove { pos -> + layoutResult.value?.let { layoutResult -> + onHover(layoutResult.getOffsetForPosition(pos)) + } + } + } } BasicText( diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/GestureDetector.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/GestureDetector.kt index 4df48ca0af..9252e8b032 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/GestureDetector.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/GestureDetector.kt @@ -92,6 +92,17 @@ suspend fun PointerInputScope.detectGesture( } } +suspend fun PointerInputScope.detectCursorMove(onMove: (Offset) -> Unit = {},) = coroutineScope { + forEachGesture { + awaitPointerEventScope { + val event = awaitPointerEvent() + if (event.type == PointerEventType.Move) { + onMove(event.changes[0].position) + } + } + } +} + private suspend fun AwaitPointerEventScope.consumeUntilUp() { do { val event = awaitPointerEvent()