android/ui: auto-resize tailnet name to fit space

Updates tailscale/corp#18202

Signed-off-by: Percy Wegmann <percy@tailscale.com>
pull/276/head
Percy Wegmann 2 months ago committed by Percy Wegmann
parent d332ce049e
commit 1719d5d558

@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
@ -245,3 +246,6 @@ val ColorScheme.searchBarColors: TextFieldColors
val TextStyle.short: TextStyle val TextStyle.short: TextStyle
get() = copy(lineHeight = 20.sp) get() = copy(lineHeight = 20.sp)
val Typography.minTextSize: TextUnit
get() = 10.sp

@ -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) }
})
}

@ -69,11 +69,13 @@ import com.tailscale.ipn.ui.model.Permissions
import com.tailscale.ipn.ui.model.Tailcfg import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.theme.disabled import com.tailscale.ipn.ui.theme.disabled
import com.tailscale.ipn.ui.theme.listItem 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.primaryListItem
import com.tailscale.ipn.ui.theme.searchBarColors import com.tailscale.ipn.ui.theme.searchBarColors
import com.tailscale.ipn.ui.theme.secondaryButton import com.tailscale.ipn.ui.theme.secondaryButton
import com.tailscale.ipn.ui.theme.short import com.tailscale.ipn.ui.theme.short
import com.tailscale.ipn.ui.theme.surfaceContainerListItem 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.Lists
import com.tailscale.ipn.ui.util.LoadingIndicator import com.tailscale.ipn.ui.util.LoadingIndicator
import com.tailscale.ipn.ui.util.PeerSet import com.tailscale.ipn.ui.util.PeerSet
@ -112,11 +114,11 @@ fun MainView(navigation: MainViewNavigation, viewModel: MainViewModel = viewMode
}, },
headlineContent = { headlineContent = {
user?.NetworkProfile?.DomainName?.let { domain -> user?.NetworkProfile?.DomainName?.let { domain ->
Text( AutoResizingText(
text = domain, text = domain,
maxLines = 1, style = MaterialTheme.typography.titleMedium.short,
overflow = TextOverflow.Ellipsis, minFontSize = MaterialTheme.typography.minTextSize,
style = MaterialTheme.typography.titleMedium.short) overflow = TextOverflow.Ellipsis)
} }
}, },
supportingContent = { supportingContent = {

@ -11,9 +11,12 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import com.tailscale.ipn.R import com.tailscale.ipn.R
import com.tailscale.ipn.ui.model.IpnLocal 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.theme.short
import com.tailscale.ipn.ui.util.AutoResizingText
// Used to decorate UserViews. // Used to decorate UserViews.
// NONE indicates no decoration // NONE indicates no decoration
@ -39,14 +42,18 @@ fun UserView(
modifier = Modifier.clickable { onClick() }, modifier = Modifier.clickable { onClick() },
leadingContent = { Avatar(profile = profile, size = 36) }, leadingContent = { Avatar(profile = profile, size = 36) },
headlineContent = { headlineContent = {
Text( AutoResizingText(
text = profile.UserProfile.DisplayName, text = profile.UserProfile.DisplayName,
style = MaterialTheme.typography.titleMedium.short) style = MaterialTheme.typography.titleMedium.short,
minFontSize = MaterialTheme.typography.minTextSize,
overflow = TextOverflow.Ellipsis)
}, },
supportingContent = { supportingContent = {
Text( AutoResizingText(
text = profile.NetworkProfile?.DomainName ?: "", text = profile.NetworkProfile?.DomainName ?: "",
style = MaterialTheme.typography.bodyMedium.short) style = MaterialTheme.typography.bodyMedium.short,
minFontSize = MaterialTheme.typography.minTextSize,
overflow = TextOverflow.Ellipsis)
}, },
trailingContent = { trailingContent = {
when (actionState) { when (actionState) {

Loading…
Cancel
Save