#4 도넛 안 캐릭터/mL 위치 - bladdySize: donutSize*0.85 → donutSize*0.85*0.75 (75% 축소) - volFontSize: 40sp → 50sp (125%) - offset 위로: -0.03 → -0.08 (더 위로) - 캐릭터-mL Spacer: 4dp → 0dp (간격 축소) → 윗공간 비던 부분 흡수, 그룹이 도넛 가운데 컴팩트 정렬 #5 게이지(stroke arc)가 도넛 외곽선 이탈 → 동일 크기 맞춤 Compose Canvas의 drawCircle/drawArc는 default로 size 경계에 stroke를 그리는데 strokeWidth의 절반이 밖으로 튀어나옴. → 명시적 inner radius 계산: outerR = size.minDimension / 2 innerR = outerR - strokeWidth / 2 drawCircle/drawArc 모두 innerR로 그림 → stroke가 size 안에 들어옴. #6 우측 InfoCard 'Session Max' → MaxRatioCard (% + 측정/capacity) 새 컴포저블 MaxRatioCard: 위: "{pct}%" (큰 글씨, urgency 색) 아래: "{measured} / {capacity} mL" 예: 47% / 235 / 500 mL #7 좌측 InfoCard 'Current' → 'Current Measurement' Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,20 +38,33 @@ fun DonutChartView(
|
||||
.semantics { contentDescription = "Bladder fill $pct percent, ${urgency.message}" }
|
||||
) {
|
||||
val strokeWidth = lineWidth.toPx()
|
||||
// Stroke 외곽이 Canvas 경계 밖으로 튀어나오는 걸 막기 위해 radius를 inner로 명시.
|
||||
// 회의 #5: 게이지가 도넛 외곽선을 이탈해 보이던 문제 해결.
|
||||
val outerR = this.size.minDimension / 2f
|
||||
val innerR = outerR - strokeWidth / 2f
|
||||
val center = androidx.compose.ui.geometry.Offset(this.size.width / 2f, this.size.height / 2f)
|
||||
|
||||
// Background track
|
||||
drawCircle(
|
||||
color = Color.Gray.copy(alpha = 0.12f),
|
||||
radius = innerR,
|
||||
center = center,
|
||||
style = Stroke(width = strokeWidth)
|
||||
)
|
||||
|
||||
// Progress arc
|
||||
// Progress arc — same radius
|
||||
if (animatedProgress > 0f) {
|
||||
val arcSize = androidx.compose.ui.geometry.Size(innerR * 2, innerR * 2)
|
||||
val arcOffset = androidx.compose.ui.geometry.Offset(
|
||||
center.x - innerR, center.y - innerR
|
||||
)
|
||||
drawArc(
|
||||
color = gradientColors.first(),
|
||||
startAngle = -90f,
|
||||
sweepAngle = 360f * animatedProgress,
|
||||
useCenter = false,
|
||||
topLeft = arcOffset,
|
||||
size = arcSize,
|
||||
style = Stroke(width = strokeWidth, cap = StrokeCap.Round)
|
||||
)
|
||||
}
|
||||
|
||||
+39
-13
@@ -1015,9 +1015,9 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
val baseDonutSize = donutFitSize
|
||||
val donutSize = baseDonutSize * 0.95f
|
||||
val donutLineWidth = if (isCompact) 26.dp else 34.dp
|
||||
val bladdySize = donutSize * 0.85f
|
||||
// ml 폰트 키움 — 회의 피드백 "숫자가 너무 작아서 안보임"
|
||||
val volFontSize = 40.sp
|
||||
// 회의 #4: 캐릭터 75% 축소, mL 125% 키움 → 컴팩트하게 도넛 가운데
|
||||
val bladdySize = donutSize * 0.85f * 0.75f
|
||||
val volFontSize = 50.sp
|
||||
|
||||
val maxVolumeForDonut = appState.piezoSettings.maxVolume.toDouble()
|
||||
val donutProgress = if (maxVolumeForDonut > 0 && maxVolumeMl > 0)
|
||||
@@ -1042,7 +1042,8 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.matchParentSize().offset(y = -(donutSize * 0.03f))
|
||||
// 회의 #4: 캐릭터+mL 그룹을 위로 더 올려 도넛 가운데 컴팩트 정렬 (이전 0.03 → 0.08)
|
||||
modifier = Modifier.matchParentSize().offset(y = -(donutSize * 0.08f))
|
||||
) {
|
||||
// Charles 833c110: Bladdy + 3-tap dev mode toggle (HomeView 패턴 통일)
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
@@ -1067,16 +1068,16 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
}
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(0.dp)) // 캐릭터-mL 간격 축소
|
||||
Text(
|
||||
// Voiding 직후엔 displayMaxVolumeMl=0이고 estimatedVolume도 0 → "0 mL"가 의미 없음 → "— mL"로
|
||||
if (displayMaxVolumeMl > 0) "${"%.0f".format(displayMaxVolumeMl)} mL"
|
||||
else if (appState.estimatedVolume > 0) "${appState.estimatedVolume} mL"
|
||||
else "— mL",
|
||||
fontSize = volFontSize,
|
||||
fontWeight = if (useScrollLayout) FontWeight.Bold else FontWeight.ExtraBold,
|
||||
fontWeight = FontWeight.ExtraBold,
|
||||
color = urgency.accentColor,
|
||||
letterSpacing = if (useScrollLayout) 0.sp else (-1).sp,
|
||||
letterSpacing = (-1).sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1087,15 +1088,20 @@ fun PiezoMonitoringView(appState: AppState) {
|
||||
val fillPct = if (maxVol > 0 && maxVolumeMl > 0) (maxVolumeMl / maxVol * 100).coerceAtMost(100.0) else 0.0
|
||||
val levelVal = if (maxVol > 0 && maxVolumeMl > 0) ((maxVolumeMl / maxVol) * BladderLevel.MAX_LEVEL).toInt().coerceIn(0, BladderLevel.MAX_LEVEL) else 0
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
// Auto 윈도우 < 5일 때는 표시 안 함
|
||||
// 좌측: Current Measurement (회의 #7)
|
||||
val showMeasurement = if (isAutoMeasuring) autoVolumeWindow.size >= 5 && lastVolumeMl > 0 else lastVolumeMl > 0
|
||||
val currentMeasurement = if (showMeasurement) "%.0f".format(lastVolumeMl) else "—"
|
||||
InfoCard("Current", currentMeasurement, "mL", urgency.accentColor, Modifier.weight(1f))
|
||||
// 우측 카드: title 명시 "Session Max", 값은 maxMeasured (단위 mL).
|
||||
// 이전엔 title이 "X mL / Y mL"로 들어가고 값에 % 표기되어 의미 모호했음.
|
||||
// % 정보는 도넛 차트가 이미 시각화 중 → 카드는 raw 숫자만.
|
||||
InfoCard("Current Measurement", currentMeasurement, "mL", urgency.accentColor, Modifier.weight(1f))
|
||||
// 우측: 측정 max / max capacity 비율 (회의 #6) — 위에 XX%, 아래 raw 분수
|
||||
val maxMeasured = if (displayMaxVolumeMl > 0) "%.0f".format(displayMaxVolumeMl) else "—"
|
||||
InfoCard("Session Max", maxMeasured, "mL", urgency.accentColor, Modifier.weight(1f))
|
||||
val capacityVal = appState.piezoSettings.maxVolume
|
||||
MaxRatioCard(
|
||||
pct = fillPct.toInt(),
|
||||
measured = maxMeasured,
|
||||
capacity = capacityVal,
|
||||
color = urgency.accentColor,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
// Control buttons: Void/Catheterize / Auto / Spot
|
||||
@@ -2137,3 +2143,23 @@ private fun InfoCard(title: String, value: String, unit: String, color: Color, m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 회의 #6: 측정 max / max capacity 비율 카드. 위에 XX%, 아래에 "측정값 / capacity mL". */
|
||||
@Composable
|
||||
private fun MaxRatioCard(pct: Int, measured: String, capacity: Int, color: Color, modifier: Modifier = Modifier) {
|
||||
Card(
|
||||
modifier = modifier.shadow(4.dp, RoundedCornerShape(14.dp), spotColor = Color.Black.copy(alpha = 0.04f)),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = MlCardBackground)
|
||||
) {
|
||||
val fs = (LocalConfiguration.current.screenWidthDp / 360f).coerceIn(1.0f, 1.8f)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = 14.dp).fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text("${pct}%", fontSize = (20 * fs).sp, fontWeight = FontWeight.Bold, color = color)
|
||||
Text("$measured / $capacity mL", fontSize = (12 * fs).sp, color = MlSecondaryText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user