From 74701a210840cbf792d1dfcb303089f30b75ad5c Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 25 Aug 2026 02:39:43 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B0=D0=BD=D0=B5=D0=BB=D1=8C=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=82=D1=83=D1=81=D0=B0=20=D1=81=D0=B4=D0=B2=D0=B8?= =?UTF-8?q?=D0=B3=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=BA=20=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D1=83=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=B5=20=D1=81=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80=D0=BE?= =?UTF-8?q?=D0=BC=20(panelOffsetX):=20=D1=86=D0=B5=D0=BD=D1=82=D1=80=20?= =?UTF-8?q?=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D0=B8=20=3D=20=D1=86=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D1=80=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5?= =?UTF-8?q?=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pw/binom/viewmate/glasses/MainActivity.kt | 6 ++++- .../viewmate/glasses/ui/GlassesMovieScreen.kt | 13 +++++++++- .../binom/viewmate/glasses/ScaleOffsetTest.kt | 26 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/MainActivity.kt b/app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/MainActivity.kt index 9f85c65..cf9e6c1 100644 --- a/app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/MainActivity.kt +++ b/app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/MainActivity.kt @@ -66,6 +66,7 @@ import pw.binom.viewmate.glasses.ui.Side import pw.binom.viewmate.glasses.ui.px import pw.binom.viewmate.glasses.ui.scaleOffsetX import pw.binom.viewmate.glasses.ui.verticalOffsetY +import pw.binom.viewmate.glasses.ui.panelOffsetX class MainActivity : BaseEventActivity() { private var resumeAfterScreenOn = false @@ -504,7 +505,10 @@ fun ConnectionScreen() { } StatusPanelCompose( - modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 10.dp), + modifier = Modifier + .align(Alignment.BottomCenter) + .offset(x = panelOffsetX(side, screenWidthPx, scale).px) + .padding(bottom = 10.dp), scale = scale, // панель масштабируется вместе с контентом, но прибита к низу ) } diff --git a/app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/ui/GlassesMovieScreen.kt b/app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/ui/GlassesMovieScreen.kt index b813519..e12f2e7 100644 --- a/app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/ui/GlassesMovieScreen.kt +++ b/app-glasses/src/main/kotlin/pw/binom/viewmate/glasses/ui/GlassesMovieScreen.kt @@ -140,7 +140,10 @@ fun GlassesMovieScreen( } StatusPanelCompose( - modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 10.dp), + modifier = Modifier + .align(Alignment.BottomCenter) + .offset(x = panelOffsetX(side, screenWidthPx, scale).px) + .padding(bottom = 10.dp), scale = scale, // панель масштабируется вместе с контентом, но прибита к низу ) } @@ -170,6 +173,14 @@ fun verticalOffsetY(screenHeightPx: Int, scale: Float, vertical: Float): Int = fun videoOffsetY(containerHeightPx: Int, videoHeightPx: Int, vertical: Float): Int = ((1f - vertical) * (containerHeightPx - videoHeightPx).coerceAtLeast(0)).toInt() +/** + * Горизонтальный сдвиг панели статуса к центру (к носу), чтобы её центр совпадал + * с центром контейнера при любом масштабе: LEFT — вправо, RIGHT — влево, + * на половину хода контейнера ((1 - scale) * W / 2). + */ +fun panelOffsetX(side: Side, screenWidthPx: Int, scale: Float): Int = + ((1f - scale) * screenWidthPx / 2f).toInt() * if (side == Side.LEFT) 1 else -1 + /** Отладочный режим: красный квадрат вместо видео (проверка масштаба/позиций скрином). Перед финалом — false. */ private const val SCALE_DEBUG_SQUARE = false diff --git a/app-glasses/src/test/kotlin/pw/binom/viewmate/glasses/ScaleOffsetTest.kt b/app-glasses/src/test/kotlin/pw/binom/viewmate/glasses/ScaleOffsetTest.kt index 07f37f1..297b555 100644 --- a/app-glasses/src/test/kotlin/pw/binom/viewmate/glasses/ScaleOffsetTest.kt +++ b/app-glasses/src/test/kotlin/pw/binom/viewmate/glasses/ScaleOffsetTest.kt @@ -6,6 +6,7 @@ import pw.binom.viewmate.glasses.ui.Side import pw.binom.viewmate.glasses.ui.scaleOffsetX import pw.binom.viewmate.glasses.ui.verticalOffsetY import pw.binom.viewmate.glasses.ui.videoOffsetY +import pw.binom.viewmate.glasses.ui.panelOffsetX /** * scaleOffsetX — смещение контейнера глаза к центру (к носу): @@ -88,4 +89,29 @@ class ScaleOffsetTest { fun videoLargerThanContainer_noNegativeOffset() { assertEquals(0, videoOffsetY(200, 268, 0f), "видео выше контейнера — хода нет") } + + /* + * panelOffsetX — панель статуса сдвигается к носу, как контейнер: + * LEFT — вправо, RIGHT — влево, на (1 - scale) * W / 2. + */ + + @Test + fun panelFullScale_noOffset() { + assertEquals(0, panelOffsetX(Side.LEFT, 640, 1f)) + assertEquals(0, panelOffsetX(Side.RIGHT, 640, 1f)) + } + + @Test + fun panelHalfScale_movesTowardNose() { + // 640 * 0.5 / 2 = 160: LEFT вправо, RIGHT влево + assertEquals(160, panelOffsetX(Side.LEFT, 640, 0.5f)) + assertEquals(-160, panelOffsetX(Side.RIGHT, 640, 0.5f)) + } + + @Test + fun panelScale07_offset() { + // 640 * 0.3 / 2 = 96 + assertEquals(96, panelOffsetX(Side.LEFT, 640, 0.7f)) + assertEquals(-96, panelOffsetX(Side.RIGHT, 640, 0.7f)) + } }