From 78fbe0b3c8db73acaf85af182bf1325405a6ad67 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Sat, 11 Apr 2026 21:59:03 +0500 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D1=82=D1=8C=20recording=20layout=201:1=20=D1=81=20?= =?UTF-8?q?Telegram=20=E2=80=94=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=BF=D1=80=D0=BE=D0=BF=D0=BE=D1=80?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=B8=20overlay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Telegram dimensions: - Circle: 48dp layout → 82dp visual (scale 1.71x), как circleRadius=41dp - Lock: 50dp→36dp pill, 70dp выше центра круга - Panel bar: full width Row с end=52dp для overlap - Blob: 1.7x scale = 82dp visual (Telegram blob minRadius) - Controls: 36dp (delete + pause) - Tooltip: 90dp левее, 70dp выше Layout architecture: - Layer 1: Panel bar (Row с clip RoundedCornerShape) - Layer 2: Circle overlay (graphicsLayer scale, NO clip) - Layer 3: Lock overlay (graphicsLayer translationY, NO clip) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ui/chats/input/ChatDetailInput.kt | 85 ++++++++++--------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt index e6b9259..7f2aed8 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt @@ -1956,7 +1956,9 @@ fun MessageInputBar( label = "record_ui_shift" ) - // Outer Box — no clip, allows children to overflow + // ── Telegram-style recording layout ── + // Telegram uses separate overlay layers (RecordCircle 194dp, ControlsView 250dp) + // We replicate with Box layers + graphicsLayer for overflow Box( modifier = Modifier .fillMaxWidth() @@ -1967,20 +1969,20 @@ fun MessageInputBar( recordingInputRowY = coordinates.positionInWindow().y } ) { - // ── Layer 1: Panel bar ── - Box( + // ── Layer 1: Panel bar (timer + center) ── + // Telegram: full-width bar, circle overlaps right edge + Row( modifier = Modifier .fillMaxWidth() .height(40.dp) - .padding(end = 44.dp) // space for circle overlap .clip(RoundedCornerShape(20.dp)) .background(recordingPanelColor) - .padding(horizontal = 13.dp) + .padding(start = 13.dp, end = 52.dp), // 52dp = half-circle overlap + verticalAlignment = Alignment.CenterVertically ) { - // Left: blink dot + timer + // Blink dot + timer Row( modifier = Modifier - .align(Alignment.CenterStart) .graphicsLayer { alpha = recordUiAlpha translationX = with(density) { recordUiShift.toPx() } @@ -1988,18 +1990,9 @@ fun MessageInputBar( verticalAlignment = Alignment.CenterVertically ) { if (recordUiState == RecordUiState.PAUSED) { - Box( - modifier = Modifier.size(28.dp), - contentAlignment = Alignment.Center - ) { - Box( - modifier = Modifier - .size(10.dp) - .clip(CircleShape) - .background( - if (isDarkTheme) Color(0xFFFF5A5A) else Color(0xFFE84D4D) - ) - ) + Box(modifier = Modifier.size(28.dp), contentAlignment = Alignment.Center) { + Box(modifier = Modifier.size(10.dp).clip(CircleShape) + .background(if (isDarkTheme) Color(0xFFFF5A5A) else Color(0xFFE84D4D))) } } else { RecordBlinkDot(isDarkTheme = isDarkTheme) @@ -2013,11 +2006,13 @@ fun MessageInputBar( ) } - // Center content: SlideToCancel or Waveform+Controls + Spacer(modifier = Modifier.width(12.dp)) + + // Center: SlideToCancel or Waveform+Controls AnimatedContent( targetState = recordUiState == RecordUiState.LOCKED || recordUiState == RecordUiState.PAUSED, modifier = Modifier - .align(Alignment.Center) + .weight(1f) .graphicsLayer { alpha = recordUiAlpha translationX = with(density) { recordUiShift.toPx() } @@ -2066,22 +2061,24 @@ fun MessageInputBar( } } - // ── Layer 2: Mic/Send circle overlay ── - // Positioned at right edge, overlapping the panel + // ── Layer 2: Circle + Lock overlay ── + // 48dp layout box at right edge; visuals overflow via graphicsLayer + // Telegram: circle center at 26dp from right, radius 41dp = 82dp visual Box( modifier = Modifier .size(48.dp) .align(Alignment.CenterEnd) + .offset(x = 4.dp) // slight overlap into right padding .zIndex(5f), contentAlignment = Alignment.Center ) { - // ── Layer 3: LockIcon above circle ── + // Lock icon: floats ~70dp above circle center (Telegram: ~92dp) if (recordUiState == RecordUiState.RECORDING || recordUiState == RecordUiState.LOCKED || recordUiState == RecordUiState.PAUSED ) { - val lockSizeDp = (50.dp - 14.dp * lockProgress) - val lockYOffset = ((-56).dp + 14.dp * lockProgress) + val lockSizeDp = 50.dp - 14.dp * lockProgress + val lockYDp = -70.dp + 14.dp * lockProgress LockIcon( lockProgress = lockProgress, isLocked = recordUiState == RecordUiState.LOCKED, @@ -2090,7 +2087,7 @@ fun MessageInputBar( modifier = Modifier .size(lockSizeDp) .graphicsLayer { - translationY = with(density) { lockYOffset.toPx() } + translationY = with(density) { lockYDp.toPx() } clip = false } .zIndex(10f) @@ -2102,8 +2099,8 @@ fun MessageInputBar( isDarkTheme = isDarkTheme, modifier = Modifier .graphicsLayer { - translationX = with(density) { (-80).dp.toPx() } - translationY = with(density) { (-56).dp.toPx() } + translationX = with(density) { (-90).dp.toPx() } + translationY = with(density) { (-70).dp.toPx() } clip = false } .zIndex(11f) @@ -2111,38 +2108,37 @@ fun MessageInputBar( } } - // Blob animation (visual-only enlargement) + // Blob: 48dp base → 1.7x = ~82dp visual (matches Telegram circleRadius 41dp) VoiceButtonBlob( voiceLevel = voiceLevel, isDarkTheme = isDarkTheme, modifier = Modifier .size(48.dp) .graphicsLayer { - scaleX = 1.8f - scaleY = 1.8f + scaleX = 1.7f + scaleY = 1.7f clip = false } ) - // Send or Mic button + // Solid circle: 48dp layout, scaled to 82dp visual val sendScale by animateFloatAsState( targetValue = if (recordUiState == RecordUiState.LOCKED || recordUiState == RecordUiState.PAUSED) 1f else 0f, animationSpec = tween(durationMillis = 150, easing = FastOutSlowInEasing), label = "send_btn_scale" ) + val circleScale = 1.71f // 48dp * 1.71 ≈ 82dp (Telegram) if (recordUiState == RecordUiState.LOCKED || recordUiState == RecordUiState.PAUSED) { Box( modifier = Modifier .size(48.dp) .graphicsLayer { - scaleX = sendScale - scaleY = sendScale - } - .shadow( - elevation = 6.dp, - shape = CircleShape, + scaleX = circleScale * sendScale + scaleY = circleScale * sendScale clip = false - ) + shadowElevation = 8f + shape = CircleShape + } .clip(CircleShape) .background(PrimaryBlue) .clickable( @@ -2161,13 +2157,18 @@ fun MessageInputBar( imageVector = TelegramSendIcon, contentDescription = "Send voice message", tint = Color.White, - modifier = Modifier.size(22.dp) + modifier = Modifier.size(24.dp) ) } } else { Box( modifier = Modifier .size(48.dp) + .graphicsLayer { + scaleX = circleScale + scaleY = circleScale + clip = false + } .clip(CircleShape) .background(PrimaryBlue), contentAlignment = Alignment.Center @@ -2176,7 +2177,7 @@ fun MessageInputBar( imageVector = if (recordMode == RecordMode.VOICE) Icons.Default.Mic else Icons.Default.Videocam, contentDescription = null, tint = Color.White, - modifier = Modifier.size(22.dp) + modifier = Modifier.size(24.dp) ) } }