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.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.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.Typography
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
@ -24,8 +29,11 @@ fun AppTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable
val typography =
Typography(
// titleMedium is styled to be slightly larger than bodyMedium for emphasis
titleMedium =
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 =
MaterialTheme.typography.bodyMedium.copy(fontSize = 16.sp, lineHeight = 26.sp))
@ -43,8 +51,10 @@ private val LightColors =
errorContainer = Color(0xFFFEF6F3), // red-0
onErrorContainer = Color(0xFF930921), // red-600
surfaceDim = Color(0xFFF7F5F4), // gray-100
surface = Color(0xFFF7F5F4), // gray-100
background = Color(0xFFF7F5F4), // gray-100
// surface = Color(0xFFF7F5F4), // gray-100
surface = Color(0xFFFFFFFF), // white,
// background = Color(0xFFF7F5F4), // gray-100
background = Color(0xFFFFFFFF), // white
surfaceBright = Color(0xFFFFFFFF), // white
surfaceContainerLowest = Color(0xFFFFFFFF), // white
surfaceContainerLow = Color(0xFFF7F5F4), // gray-100
@ -93,3 +103,51 @@ val ColorScheme.successContainer: Color
val ColorScheme.onSuccessContainer: Color
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.unit.dp
import com.tailscale.ipn.R
import com.tailscale.ipn.ui.theme.ts_color_light_blue
@Composable
fun ClipboardValueView(
@ -56,8 +55,7 @@ fun ClipboardValueView(
Icon(
painterResource(R.drawable.clipboard),
stringResource(R.string.copy_to_clipboard),
modifier = Modifier.width(24.dp).height(24.dp),
tint = ts_color_light_blue)
modifier = Modifier.width(24.dp).height(24.dp))
})
}
}

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

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

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

Loading…
Cancel
Save