From d70b13ef33a76b5da891bbe3e95d25ccff675226 Mon Sep 17 00:00:00 2001 From: nrobi144 Date: Tue, 17 Mar 2026 17:09:40 +0200 Subject: [PATCH] fix(media): properly constrain video height within NoteCard Replace aspectRatio modifier with BoxWithConstraints that manually calculates height from aspect ratio and clamps it to the max height constraint. Video now stays within its allocated space in the card, so controls are accessible and content below is not overlapped. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../desktop/ui/media/DesktopVideoPlayer.kt | 148 +++++++++--------- 1 file changed, 78 insertions(+), 70 deletions(-) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/media/DesktopVideoPlayer.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/media/DesktopVideoPlayer.kt index d904c53bd..141081643 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/media/DesktopVideoPlayer.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/media/DesktopVideoPlayer.kt @@ -23,9 +23,10 @@ package com.vitorpamplona.amethyst.desktop.ui.media import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -248,77 +249,84 @@ fun DesktopVideoPlayer( return } - Box( - modifier = - modifier - .aspectRatio(aspectRatio) - .background( - MaterialTheme.colorScheme.surfaceContainerHigh, - RoundedCornerShape(8.dp), - ), - contentAlignment = Alignment.Center, - ) { - val displayBitmap = frame ?: thumbnail - displayBitmap?.let { bitmap -> - Image( - bitmap = bitmap, - contentDescription = "Video", - modifier = - Modifier - .fillMaxSize() - .clip(RoundedCornerShape(8.dp)), - contentScale = ContentScale.Fit, + BoxWithConstraints(modifier = modifier) { + // Calculate height from aspect ratio, clamped to max constraints + val desiredHeight = maxWidth / aspectRatio + val constrainedHeight = if (constraints.hasBoundedHeight) minOf(desiredHeight, maxHeight) else desiredHeight + + Box( + modifier = + Modifier + .fillMaxWidth() + .height(constrainedHeight) + .background( + MaterialTheme.colorScheme.surfaceContainerHigh, + RoundedCornerShape(8.dp), + ), + contentAlignment = Alignment.Center, + ) { + val displayBitmap = frame ?: thumbnail + displayBitmap?.let { bitmap -> + Image( + bitmap = bitmap, + contentDescription = "Video", + modifier = + Modifier + .fillMaxSize() + .clip(RoundedCornerShape(8.dp)), + contentScale = ContentScale.Fit, + ) + } + + VideoControls( + isPlaying = isPlaying, + isBuffering = isBuffering, + position = position, + duration = duration, + currentTime = currentTime, + volume = volume, + isMuted = isMuted, + viewMode = viewMode, + onPlayPause = { + val p = player + if (p != null) { + if (isPlaying) { + p.controls().pause() + } else { + ActiveMediaManager.activate(playerId) + if (position <= 0f && !p.status().isPlaying) { + p.media().play(url) + isBuffering = true + } else { + p.controls().play() + } + } + } else { + // First play — activate lazy init + activated = true + } + }, + onSeek = { pos -> + player?.controls()?.setPosition(pos) + }, + onVolumeChange = { vol -> + volume = vol + player?.audio()?.setVolume(vol) + }, + onMuteToggle = { + isMuted = !isMuted + player?.audio()?.isMute = isMuted + }, + onFullscreen = + if (onFullscreen != null) { + { onFullscreen(position) } + } else { + null + }, + onViewModeChange = onViewModeChange, + trailingControls = trailingControls, ) } - - VideoControls( - isPlaying = isPlaying, - isBuffering = isBuffering, - position = position, - duration = duration, - currentTime = currentTime, - volume = volume, - isMuted = isMuted, - viewMode = viewMode, - onPlayPause = { - val p = player - if (p != null) { - if (isPlaying) { - p.controls().pause() - } else { - ActiveMediaManager.activate(playerId) - if (position <= 0f && !p.status().isPlaying) { - p.media().play(url) - isBuffering = true - } else { - p.controls().play() - } - } - } else { - // First play — activate lazy init - activated = true - } - }, - onSeek = { pos -> - player?.controls()?.setPosition(pos) - }, - onVolumeChange = { vol -> - volume = vol - player?.audio()?.setVolume(vol) - }, - onMuteToggle = { - isMuted = !isMuted - player?.audio()?.isMute = isMuted - }, - onFullscreen = - if (onFullscreen != null) { - { onFullscreen(position) } - } else { - null - }, - onViewModeChange = onViewModeChange, - trailingControls = trailingControls, - ) } }