From 1719d5d558d62e618f735d0508fb27093ce1d4fd Mon Sep 17 00:00:00 2001 From: Percy Wegmann Date: Tue, 2 Apr 2024 13:31:27 -0500 Subject: [PATCH] android/ui: auto-resize tailnet name to fit space Updates tailscale/corp#18202 Signed-off-by: Percy Wegmann --- .../java/com/tailscale/ipn/ui/theme/Theme.kt | 4 + .../tailscale/ipn/ui/util/AutoResizingText.kt | 78 +++++++++++++++++++ .../com/tailscale/ipn/ui/view/MainView.kt | 10 ++- .../com/tailscale/ipn/ui/view/UserView.kt | 15 +++- 4 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 android/src/main/java/com/tailscale/ipn/ui/util/AutoResizingText.kt diff --git a/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt b/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt index 38213cc..1b1fef8 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.sp import com.google.accompanist.systemuicontroller.rememberSystemUiController @@ -245,3 +246,6 @@ val ColorScheme.searchBarColors: TextFieldColors val TextStyle.short: TextStyle get() = copy(lineHeight = 20.sp) + +val Typography.minTextSize: TextUnit + get() = 10.sp diff --git a/android/src/main/java/com/tailscale/ipn/ui/util/AutoResizingText.kt b/android/src/main/java/com/tailscale/ipn/ui/util/AutoResizingText.kt new file mode 100644 index 0000000..709bc58 --- /dev/null +++ b/android/src/main/java/com/tailscale/ipn/ui/util/AutoResizingText.kt @@ -0,0 +1,78 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package com.tailscale.ipn.ui.util + +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.TextUnit + +// AutoResizingText automatically resizes text up to the specified minFontSize in order to avoid +// overflowing. It is based on https://stackoverflow.com/a/66090448 licensed under CC BY-SA 4.0. +@Composable +fun AutoResizingText( + text: String, + minFontSize: TextUnit, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + maxLines: Int = 1, + onTextLayout: ((TextLayoutResult) -> Unit)? = null, + style: TextStyle = LocalTextStyle.current +) { + var textStyle = remember { mutableStateOf(style) } + var textOverflow = remember { mutableStateOf(TextOverflow.Clip) } + var readyToDraw = remember { mutableStateOf(false) } + + Text( + text = text, + modifier = modifier.drawWithContent { if (readyToDraw.value) drawContent() }, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = textOverflow.value, + maxLines = maxLines, + softWrap = false, + style = textStyle.value, + onTextLayout = { result -> + if (result.didOverflowWidth) { + var newSize = textStyle.value.fontSize * 0.9 + if (newSize < minFontSize) { + newSize = minFontSize + textOverflow.value = overflow + } + textStyle.value = textStyle.value.copy(fontSize = newSize) + } else { + readyToDraw.value = true + } + onTextLayout?.let { it(result) } + }) +} diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt index ab33b8b..4996486 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt @@ -69,11 +69,13 @@ import com.tailscale.ipn.ui.model.Permissions import com.tailscale.ipn.ui.model.Tailcfg import com.tailscale.ipn.ui.theme.disabled import com.tailscale.ipn.ui.theme.listItem +import com.tailscale.ipn.ui.theme.minTextSize import com.tailscale.ipn.ui.theme.primaryListItem import com.tailscale.ipn.ui.theme.searchBarColors import com.tailscale.ipn.ui.theme.secondaryButton import com.tailscale.ipn.ui.theme.short import com.tailscale.ipn.ui.theme.surfaceContainerListItem +import com.tailscale.ipn.ui.util.AutoResizingText import com.tailscale.ipn.ui.util.Lists import com.tailscale.ipn.ui.util.LoadingIndicator import com.tailscale.ipn.ui.util.PeerSet @@ -112,11 +114,11 @@ fun MainView(navigation: MainViewNavigation, viewModel: MainViewModel = viewMode }, headlineContent = { user?.NetworkProfile?.DomainName?.let { domain -> - Text( + AutoResizingText( text = domain, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.titleMedium.short) + style = MaterialTheme.typography.titleMedium.short, + minFontSize = MaterialTheme.typography.minTextSize, + overflow = TextOverflow.Ellipsis) } }, supportingContent = { diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/UserView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/UserView.kt index 5360057..578d41d 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/UserView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/UserView.kt @@ -11,9 +11,12 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import com.tailscale.ipn.R import com.tailscale.ipn.ui.model.IpnLocal +import com.tailscale.ipn.ui.theme.minTextSize import com.tailscale.ipn.ui.theme.short +import com.tailscale.ipn.ui.util.AutoResizingText // Used to decorate UserViews. // NONE indicates no decoration @@ -39,14 +42,18 @@ fun UserView( modifier = Modifier.clickable { onClick() }, leadingContent = { Avatar(profile = profile, size = 36) }, headlineContent = { - Text( + AutoResizingText( text = profile.UserProfile.DisplayName, - style = MaterialTheme.typography.titleMedium.short) + style = MaterialTheme.typography.titleMedium.short, + minFontSize = MaterialTheme.typography.minTextSize, + overflow = TextOverflow.Ellipsis) }, supportingContent = { - Text( + AutoResizingText( text = profile.NetworkProfile?.DomainName ?: "", - style = MaterialTheme.typography.bodyMedium.short) + style = MaterialTheme.typography.bodyMedium.short, + minFontSize = MaterialTheme.typography.minTextSize, + overflow = TextOverflow.Ellipsis) }, trailingContent = { when (actionState) {