More styling progress

Signed-off-by: Percy Wegmann <percy@tailscale.com>
ox/styling_bak
Percy Wegmann 2 years ago
parent d594915eb5
commit 89b3a04c0a
No known key found for this signature in database
GPG Key ID: 29D8CDEB4C13D48B

@ -5,7 +5,12 @@ package com.tailscale.ipn.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme import androidx.compose.material3.ColorScheme
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItemColors
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.Typography import androidx.compose.material3.Typography
import androidx.compose.material3.darkColorScheme import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme import androidx.compose.material3.lightColorScheme
@ -24,8 +29,11 @@ fun AppTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable
val typography = val typography =
Typography( Typography(
// titleMedium is styled to be slightly larger than bodyMedium for emphasis
titleMedium = titleMedium =
MaterialTheme.typography.titleMedium.copy(fontSize = 18.sp, lineHeight = 26.sp), MaterialTheme.typography.titleMedium.copy(fontSize = 18.sp, lineHeight = 26.sp),
// bodyMedium is styled to use same line height as titleMedium to ensure even vertical
// margins in list items.
bodyMedium = bodyMedium =
MaterialTheme.typography.bodyMedium.copy(fontSize = 16.sp, lineHeight = 26.sp)) MaterialTheme.typography.bodyMedium.copy(fontSize = 16.sp, lineHeight = 26.sp))
@ -43,8 +51,10 @@ private val LightColors =
errorContainer = Color(0xFFFEF6F3), // red-0 errorContainer = Color(0xFFFEF6F3), // red-0
onErrorContainer = Color(0xFF930921), // red-600 onErrorContainer = Color(0xFF930921), // red-600
surfaceDim = Color(0xFFF7F5F4), // gray-100 surfaceDim = Color(0xFFF7F5F4), // gray-100
surface = Color(0xFFF7F5F4), // gray-100 // surface = Color(0xFFF7F5F4), // gray-100
background = Color(0xFFF7F5F4), // gray-100 surface = Color(0xFFFFFFFF), // white,
// background = Color(0xFFF7F5F4), // gray-100
background = Color(0xFFFFFFFF), // white
surfaceBright = Color(0xFFFFFFFF), // white surfaceBright = Color(0xFFFFFFFF), // white
surfaceContainerLowest = Color(0xFFFFFFFF), // white surfaceContainerLowest = Color(0xFFFFFFFF), // white
surfaceContainerLow = Color(0xFFF7F5F4), // gray-100 surfaceContainerLow = Color(0xFFF7F5F4), // gray-100
@ -93,3 +103,51 @@ val ColorScheme.successContainer: Color
val ColorScheme.onSuccessContainer: Color val ColorScheme.onSuccessContainer: Color
get() = Color(0xFF0E4B3B) // green-600 get() = Color(0xFF0E4B3B) // green-600
/**
* Main color scheme for list items, uses onPrimaryContainer color for leading and trailing icons.
*/
val ColorScheme.listItem: ListItemColors
@Composable
get() {
val default = ListItemDefaults.colors()
return ListItemColors(
containerColor = default.containerColor,
headlineColor = default.headlineColor,
leadingIconColor = MaterialTheme.colorScheme.onPrimaryContainer,
overlineColor = default.overlineColor,
supportingTextColor = default.supportingTextColor,
trailingIconColor = MaterialTheme.colorScheme.onPrimaryContainer,
disabledHeadlineColor = default.disabledHeadlineColor,
disabledLeadingIconColor = default.disabledLeadingIconColor,
disabledTrailingIconColor = default.disabledTrailingIconColor)
}
/** Color scheme for list items that should be styled as a surface container. */
val ColorScheme.containerListItem: ListItemColors
@Composable
get() {
val default = ListItemDefaults.colors()
return ListItemColors(
containerColor = MaterialTheme.colorScheme.surfaceContainer,
headlineColor = MaterialTheme.colorScheme.onSurface,
leadingIconColor = MaterialTheme.colorScheme.onSurface,
overlineColor = MaterialTheme.colorScheme.onSurface,
supportingTextColor = MaterialTheme.colorScheme.onSurface,
trailingIconColor = MaterialTheme.colorScheme.onSurface,
disabledHeadlineColor = default.disabledHeadlineColor,
disabledLeadingIconColor = default.disabledLeadingIconColor,
disabledTrailingIconColor = default.disabledTrailingIconColor)
}
/** Main color scheme for top app bar, styles it as a surface container. */
@OptIn(ExperimentalMaterial3Api::class)
val ColorScheme.topAppBar: TopAppBarColors
@Composable
get() =
TopAppBarDefaults.topAppBarColors()
.copy(
containerColor = MaterialTheme.colorScheme.surfaceContainer,
navigationIconContentColor = MaterialTheme.colorScheme.onSurface,
titleContentColor = MaterialTheme.colorScheme.onSurface,
)

@ -22,7 +22,6 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.tailscale.ipn.R import com.tailscale.ipn.R
import com.tailscale.ipn.ui.theme.ts_color_light_blue
@Composable @Composable
fun ClipboardValueView( fun ClipboardValueView(
@ -56,8 +55,7 @@ fun ClipboardValueView(
Icon( Icon(
painterResource(R.drawable.clipboard), painterResource(R.drawable.clipboard),
stringResource(R.string.copy_to_clipboard), stringResource(R.string.copy_to_clipboard),
modifier = Modifier.width(24.dp).height(24.dp), modifier = Modifier.width(24.dp).height(24.dp))
tint = ts_color_light_blue)
}) })
} }
} }

@ -26,7 +26,7 @@ object Lists {
@Composable @Composable
fun ItemDivider() { fun ItemDivider() {
HorizontalDivider(color = MaterialTheme.colorScheme.outline) HorizontalDivider(color = MaterialTheme.colorScheme.surfaceContainer)
} }
} }

@ -44,6 +44,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
@ -56,6 +57,9 @@ import com.tailscale.ipn.ui.model.IpnLocal
import com.tailscale.ipn.ui.model.Permission import com.tailscale.ipn.ui.model.Permission
import com.tailscale.ipn.ui.model.Permissions 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.containerListItem
import com.tailscale.ipn.ui.theme.listItem
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
import com.tailscale.ipn.ui.util.flag import com.tailscale.ipn.ui.util.flag
@ -84,11 +88,13 @@ fun MainView(navigation: MainViewNavigation, viewModel: MainViewModel = viewMode
val username = viewModel.userName val username = viewModel.userName
ListItem( ListItem(
colors = MaterialTheme.colorScheme.containerListItem,
leadingContent = { leadingContent = {
val isOn = viewModel.vpnToggleState.collectAsState(initial = false) val isOn = viewModel.vpnToggleState.collectAsState(initial = false)
if (state.value != Ipn.State.NoState) { TintedSwitch(
TintedSwitch(onCheckedChange = { viewModel.toggleVpn() }, checked = isOn.value) onCheckedChange = { viewModel.toggleVpn() },
} checked = isOn.value,
enabled = state.value != Ipn.State.NoState)
}, },
headlineContent = { headlineContent = {
if (username.isNotEmpty()) { if (username.isNotEmpty()) {
@ -119,10 +125,8 @@ fun MainView(navigation: MainViewNavigation, viewModel: MainViewModel = viewMode
PromptPermissionsIfNecessary(permissions = Permissions.all) PromptPermissionsIfNecessary(permissions = Permissions.all)
Row(modifier = Modifier.padding(top = 10.dp, bottom = 20.dp)) { ExitNodeStatus(navAction = navigation.onNavigateToExitNodes, viewModel = viewModel)
ExitNodeStatus(
navAction = navigation.onNavigateToExitNodes, viewModel = viewModel)
}
PeerList( PeerList(
viewModel = viewModel, viewModel = viewModel,
onNavigateToPeerDetails = navigation.onNavigateToPeerDetails, onNavigateToPeerDetails = navigation.onNavigateToPeerDetails,
@ -146,30 +150,32 @@ fun ExitNodeStatus(navAction: () -> Unit, viewModel: MainViewModel) {
val exitNodeId = prefs.value?.ExitNodeID val exitNodeId = prefs.value?.ExitNodeID
val peer = exitNodeId?.let { id -> netmap.value?.Peers?.find { it.StableID == id } } val peer = exitNodeId?.let { id -> netmap.value?.Peers?.find { it.StableID == id } }
val location = peer?.Hostinfo?.Location val location = peer?.Hostinfo?.Location
val name = peer?.Name val name = peer?.ComputedName
Box(modifier = Modifier.background(color = MaterialTheme.colorScheme.surfaceContainer)) {
Box( Box(
modifier = modifier =
Modifier.padding(horizontal = 16.dp) Modifier.padding(start = 16.dp, end = 16.dp, top = 4.dp, bottom = 16.dp)
.clip(shape = RoundedCornerShape(10.dp, 10.dp, 10.dp, 10.dp)) .clip(shape = RoundedCornerShape(10.dp, 10.dp, 10.dp, 10.dp))
.background(MaterialTheme.colorScheme.background)
.fillMaxWidth()) { .fillMaxWidth()) {
ListItem( ListItem(
modifier = Modifier.clickable { navAction() }, modifier = Modifier.clickable { navAction() },
headlineContent = { overlineContent = {
Text( Text(
stringResource(R.string.exit_node), stringResource(R.string.exit_node),
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.bodyMedium,
) )
}, },
supportingContent = { headlineContent = {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Text( Text(
text = text =
location?.let { "${it.CountryCode?.flag()} ${it.Country} - ${it.City}" } location?.let { "${it.CountryCode?.flag()} ${it.Country} - ${it.City}" }
?: name ?: name
?: stringResource(id = R.string.none), ?: stringResource(id = R.string.none),
style = MaterialTheme.typography.bodyLarge) style = MaterialTheme.typography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis)
Icon( Icon(
Icons.Outlined.ArrowDropDown, Icons.Outlined.ArrowDropDown,
null, null,
@ -184,6 +190,7 @@ fun ExitNodeStatus(navAction: () -> Unit, viewModel: MainViewModel) {
} }
}) })
} }
}
} }
@Composable @Composable
@ -278,7 +285,6 @@ fun PeerList(
) { ) {
val peerList = viewModel.peers.collectAsState(initial = emptyList<PeerSet>()) val peerList = viewModel.peers.collectAsState(initial = emptyList<PeerSet>())
val searchTermStr by viewModel.searchTerm.collectAsState(initial = "") val searchTermStr by viewModel.searchTerm.collectAsState(initial = "")
val stateVal = viewModel.ipnState.collectAsState(initial = Ipn.State.NoState)
val netmap = viewModel.netmap.collectAsState() val netmap = viewModel.netmap.collectAsState()
SearchBar( SearchBar(
@ -298,6 +304,7 @@ fun PeerList(
SearchBarDefaults.colors( SearchBarDefaults.colors(
containerColor = Color.Transparent, dividerColor = Color.Transparent), containerColor = Color.Transparent, dividerColor = Color.Transparent),
modifier = Modifier.fillMaxWidth()) { modifier = Modifier.fillMaxWidth()) {
Lists.ItemDivider()
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
) { ) {
@ -315,10 +322,9 @@ fun PeerList(
itemsWithDividers(peerSet.peers, key = { it.StableID }) { peer -> itemsWithDividers(peerSet.peers, key = { it.StableID }) { peer ->
ListItem( ListItem(
modifier = Modifier.clickable { onNavigateToPeerDetails(peer) }, modifier = Modifier.clickable { onNavigateToPeerDetails(peer) },
colors = MaterialTheme.colorScheme.listItem,
headlineContent = { headlineContent = {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
// By definition, SelfPeer is online since we will not show the peer list
// unless you're connected.
Box( Box(
modifier = modifier =
Modifier.size(10.dp) Modifier.size(10.dp)

@ -28,6 +28,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.tailscale.ipn.BuildConfig import com.tailscale.ipn.BuildConfig
import com.tailscale.ipn.R import com.tailscale.ipn.R
import com.tailscale.ipn.ui.Links import com.tailscale.ipn.ui.Links
import com.tailscale.ipn.ui.theme.listItem
import com.tailscale.ipn.ui.util.Lists import com.tailscale.ipn.ui.util.Lists
import com.tailscale.ipn.ui.viewModel.Setting import com.tailscale.ipn.ui.viewModel.Setting
import com.tailscale.ipn.ui.viewModel.SettingType import com.tailscale.ipn.ui.viewModel.SettingType
@ -104,6 +105,7 @@ private fun TextRow(setting: Setting) {
val enabled = setting.enabled.collectAsState().value val enabled = setting.enabled.collectAsState().value
ListItem( ListItem(
modifier = Modifier.clickable { if (enabled) setting.onClick() }, modifier = Modifier.clickable { if (enabled) setting.onClick() },
colors = MaterialTheme.colorScheme.listItem,
headlineContent = { headlineContent = {
Text( Text(
setting.title ?: stringResource(setting.titleRes), setting.title ?: stringResource(setting.titleRes),
@ -119,6 +121,7 @@ private fun SwitchRow(setting: Setting) {
val swVal = setting.isOn?.collectAsState()?.value ?: false val swVal = setting.isOn?.collectAsState()?.value ?: false
ListItem( ListItem(
modifier = Modifier.clickable { if (enabled) setting.onClick() }, modifier = Modifier.clickable { if (enabled) setting.onClick() },
colors = MaterialTheme.colorScheme.listItem,
headlineContent = { headlineContent = {
Text( Text(
setting.title ?: stringResource(setting.titleRes), setting.title ?: stringResource(setting.titleRes),
@ -134,6 +137,7 @@ private fun SwitchRow(setting: Setting) {
private fun NavRow(setting: Setting) { private fun NavRow(setting: Setting) {
ListItem( ListItem(
modifier = Modifier.clickable { setting.onClick() }, modifier = Modifier.clickable { setting.onClick() },
colors = MaterialTheme.colorScheme.listItem,
headlineContent = { headlineContent = {
Text( Text(
setting.title ?: stringResource(setting.titleRes), setting.title ?: stringResource(setting.titleRes),

@ -14,12 +14,14 @@ import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
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.unit.dp import androidx.compose.ui.unit.dp
import com.tailscale.ipn.ui.theme.topAppBar
import com.tailscale.ipn.ui.theme.ts_color_light_blue import com.tailscale.ipn.ui.theme.ts_color_light_blue
data class BackNavigation( data class BackNavigation(
@ -32,6 +34,7 @@ data class BackNavigation(
fun Header(@StringRes title: Int = 0, titleText: String? = null, onBack: (() -> Unit)? = null) { fun Header(@StringRes title: Int = 0, titleText: String? = null, onBack: (() -> Unit)? = null) {
TopAppBar( TopAppBar(
title = { Text(titleText ?: stringResource(title)) }, title = { Text(titleText ?: stringResource(title)) },
colors = MaterialTheme.colorScheme.topAppBar,
navigationIcon = { onBack?.let { BackArrow(action = it) } }, navigationIcon = { onBack?.let { BackArrow(action = it) } },
) )
} }

Loading…
Cancel
Save