mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Resize & reshape buttons (#380)
Adds a new button implementation which allows for icon buttons to be circular. Overall the buttons are reduced in size. Also, the playback controls are are reduced in size, buttons are smaller and vertical padding is reduced. 
This commit is contained in:
parent
ac10517028
commit
793f9802e4
19 changed files with 492 additions and 206 deletions
|
|
@ -1,7 +1,6 @@
|
|||
package com.github.damontecres.wholphin.ui
|
||||
|
||||
import android.content.res.Configuration.UI_MODE_TYPE_TELEVISION
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.Font
|
||||
|
|
@ -66,14 +65,6 @@ val SlimItemFields =
|
|||
ItemFields.SORT_NAME,
|
||||
)
|
||||
|
||||
val DefaultButtonPadding =
|
||||
PaddingValues(
|
||||
start = 12.dp / 2,
|
||||
top = 10.dp / 2,
|
||||
end = 16.dp / 2,
|
||||
bottom = 10.dp / 2,
|
||||
)
|
||||
|
||||
object Cards {
|
||||
val height2x3 = 172.dp
|
||||
val playedPercentHeight = 6.dp
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
// This file was inspired by related Button source files from
|
||||
// https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/tv/tv-material/src/main/java/androidx/tv/material3
|
||||
|
||||
package com.github.damontecres.wholphin.ui.components
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.NonRestartableComposable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.Border
|
||||
import androidx.tv.material3.ClickableSurfaceBorder
|
||||
import androidx.tv.material3.ClickableSurfaceColors
|
||||
import androidx.tv.material3.ClickableSurfaceDefaults
|
||||
import androidx.tv.material3.ClickableSurfaceGlow
|
||||
import androidx.tv.material3.ClickableSurfaceScale
|
||||
import androidx.tv.material3.ClickableSurfaceShape
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.ProvideTextStyle
|
||||
import androidx.tv.material3.Surface
|
||||
import androidx.tv.material3.Text
|
||||
|
||||
@Composable
|
||||
fun Button(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
enabled: Boolean = true,
|
||||
scale: ClickableSurfaceScale = ClickableSurfaceDefaults.scale(),
|
||||
glow: ClickableSurfaceGlow = ClickableSurfaceDefaults.glow(),
|
||||
shape: ClickableSurfaceShape =
|
||||
ClickableSurfaceDefaults.shape(
|
||||
shape = CircleShape,
|
||||
),
|
||||
colors: ClickableSurfaceColors =
|
||||
ClickableSurfaceDefaults.colors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.8f),
|
||||
contentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
|
||||
focusedContainerColor = MaterialTheme.colorScheme.onSurface,
|
||||
focusedContentColor = MaterialTheme.colorScheme.inverseOnSurface,
|
||||
pressedContainerColor = MaterialTheme.colorScheme.onSurface,
|
||||
pressedContentColor = MaterialTheme.colorScheme.inverseOnSurface,
|
||||
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f),
|
||||
disabledContentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f),
|
||||
),
|
||||
tonalElevation: Dp = 0.dp,
|
||||
border: ClickableSurfaceBorder =
|
||||
ClickableSurfaceDefaults.border(
|
||||
border = Border.None,
|
||||
focusedBorder = Border.None,
|
||||
pressedBorder = Border.None,
|
||||
disabledBorder = Border.None,
|
||||
focusedDisabledBorder =
|
||||
Border(
|
||||
border =
|
||||
BorderStroke(
|
||||
width = 1.5.dp,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f),
|
||||
),
|
||||
shape = CircleShape,
|
||||
),
|
||||
),
|
||||
contentPadding: PaddingValues = DefaultButtonPadding,
|
||||
contentHeight: Dp = MinButtonSize,
|
||||
interactionSource: MutableInteractionSource? = null,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier.semantics { role = Role.Button },
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
enabled = enabled,
|
||||
scale = scale,
|
||||
glow = glow,
|
||||
shape = shape,
|
||||
colors = colors,
|
||||
tonalElevation = tonalElevation,
|
||||
border = border,
|
||||
interactionSource = interactionSource,
|
||||
) {
|
||||
ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(contentPadding)
|
||||
.height(contentHeight),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@NonRestartableComposable
|
||||
fun TextButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
enabled: Boolean = true,
|
||||
contentPadding: PaddingValues =
|
||||
PaddingValues(
|
||||
start = 8.dp,
|
||||
top = 4.dp,
|
||||
end = 8.dp,
|
||||
bottom = 4.dp,
|
||||
),
|
||||
contentHeight: Dp = 32.dp,
|
||||
interactionSource: MutableInteractionSource? = null,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) = Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
onLongClick = onLongClick,
|
||||
enabled = enabled,
|
||||
contentPadding = contentPadding,
|
||||
contentHeight = contentHeight,
|
||||
interactionSource = interactionSource,
|
||||
content = content,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun TextButton(
|
||||
@StringRes stringRes: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
enabled: Boolean = true,
|
||||
contentPadding: PaddingValues =
|
||||
PaddingValues(
|
||||
start = 8.dp,
|
||||
top = 4.dp,
|
||||
end = 8.dp,
|
||||
bottom = 4.dp,
|
||||
),
|
||||
contentHeight: Dp = 32.dp,
|
||||
interactionSource: MutableInteractionSource? = null,
|
||||
) = Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
onLongClick = onLongClick,
|
||||
enabled = enabled,
|
||||
contentPadding = contentPadding,
|
||||
contentHeight = contentHeight,
|
||||
interactionSource = interactionSource,
|
||||
content = {
|
||||
Text(text = stringResource(stringRes))
|
||||
},
|
||||
)
|
||||
|
||||
val DefaultButtonPadding =
|
||||
PaddingValues(
|
||||
start = 4.dp,
|
||||
top = 4.dp,
|
||||
end = 4.dp,
|
||||
bottom = 4.dp,
|
||||
)
|
||||
|
|
@ -45,7 +45,6 @@ import androidx.compose.ui.unit.Dp
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.ListItem
|
||||
import androidx.tv.material3.LocalContentColor
|
||||
|
|
@ -443,23 +442,17 @@ fun ConfirmDialogContent(
|
|||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.cancel,
|
||||
onClick = onCancel,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.cancel),
|
||||
)
|
||||
}
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.confirm,
|
||||
onClick = onConfirm,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.confirm),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun chooseVersionParams(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.R
|
||||
|
|
@ -56,15 +55,12 @@ fun ErrorMessage(
|
|||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.send_app_logs,
|
||||
onClick = {
|
||||
viewModel.sendLogs()
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.send_app_logs),
|
||||
)
|
||||
}
|
||||
message?.let {
|
||||
Text(
|
||||
text = it,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,13 @@ import androidx.compose.animation.AnimatedVisibility
|
|||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSizeIn
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
|
|
@ -18,17 +22,16 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalResources
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
|
|
@ -46,8 +49,9 @@ import com.github.damontecres.wholphin.data.filter.PlayedFilter
|
|||
import com.github.damontecres.wholphin.data.filter.VideoTypeFilter
|
||||
import com.github.damontecres.wholphin.data.filter.YearFilter
|
||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||
import com.github.damontecres.wholphin.ui.DefaultButtonPadding
|
||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import java.util.UUID
|
||||
|
||||
|
|
@ -353,13 +357,23 @@ fun ExpandableFilterButton(
|
|||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
iconColor: Color = Color.Unspecified,
|
||||
) {
|
||||
val resources = LocalResources.current
|
||||
val isFocused = interactionSource.collectIsFocusedAsState().value
|
||||
Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier.heightIn(min = 40.dp),
|
||||
contentPadding = DefaultButtonPadding,
|
||||
modifier =
|
||||
modifier.requiredSizeIn(
|
||||
minWidth = MinButtonSize,
|
||||
minHeight = MinButtonSize,
|
||||
maxHeight = MinButtonSize,
|
||||
),
|
||||
contentPadding = PaddingValues(0.dp), // DefaultButtonPadding,
|
||||
interactionSource = interactionSource,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.widthIn(min = if (filterCount > 0 || isFocused) (MinButtonSize - 12.dp) else MinButtonSize),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.fa_filter),
|
||||
|
|
@ -368,20 +382,39 @@ fun ExpandableFilterButton(
|
|||
fontSize = 16.sp,
|
||||
fontFamily = FontAwesome,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(filterCount > 0) {
|
||||
Text(
|
||||
text = filterCount.toString(),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
modifier = Modifier.padding(end = 4.dp),
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(isFocused) {
|
||||
Text(
|
||||
text = stringResource(R.string.filter),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
modifier = Modifier.padding(start = 4.dp, end = 4.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewTvSpec
|
||||
@Composable
|
||||
private fun ExpandableFilterButtonPreview() {
|
||||
WholphinTheme {
|
||||
Column {
|
||||
ExpandableFilterButton(
|
||||
filterCount = 2,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
ExpandableFilterButton(
|
||||
filterCount = 0,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,14 @@ import androidx.compose.foundation.focusGroup
|
|||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSizeIn
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.material.icons.Icons
|
||||
|
|
@ -18,6 +22,7 @@ import androidx.compose.material.icons.filled.PlayArrow
|
|||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.FocusState
|
||||
|
|
@ -31,16 +36,18 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.ui.DefaultButtonPadding
|
||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.theme.PreviewInteractionSource
|
||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||
import org.jellyfin.sdk.model.api.SortOrder
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
|
|
@ -77,7 +84,8 @@ fun ExpandablePlayButtons(
|
|||
playOnClick,
|
||||
Modifier
|
||||
.onFocusChanged(buttonOnFocusChanged)
|
||||
.focusRequester(firstFocus),
|
||||
.focusRequester(firstFocus)
|
||||
.animateItem(),
|
||||
)
|
||||
}
|
||||
item("restart") {
|
||||
|
|
@ -86,7 +94,9 @@ fun ExpandablePlayButtons(
|
|||
Duration.ZERO,
|
||||
Icons.Default.Refresh,
|
||||
playOnClick,
|
||||
Modifier.onFocusChanged(buttonOnFocusChanged),
|
||||
Modifier
|
||||
.onFocusChanged(buttonOnFocusChanged)
|
||||
.animateItem(),
|
||||
mirrorIcon = true,
|
||||
)
|
||||
}
|
||||
|
|
@ -99,7 +109,8 @@ fun ExpandablePlayButtons(
|
|||
playOnClick,
|
||||
Modifier
|
||||
.onFocusChanged(buttonOnFocusChanged)
|
||||
.focusRequester(firstFocus),
|
||||
.focusRequester(firstFocus)
|
||||
.animateItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -110,7 +121,10 @@ fun ExpandablePlayButtons(
|
|||
title = if (watched) R.string.mark_unwatched else R.string.mark_watched,
|
||||
iconStringRes = if (watched) R.string.fa_eye else R.string.fa_eye_slash,
|
||||
onClick = watchOnClick,
|
||||
modifier = Modifier.onFocusChanged(buttonOnFocusChanged),
|
||||
modifier =
|
||||
Modifier
|
||||
.onFocusChanged(buttonOnFocusChanged)
|
||||
.animateItem(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +135,10 @@ fun ExpandablePlayButtons(
|
|||
iconStringRes = R.string.fa_heart,
|
||||
onClick = favoriteOnClick,
|
||||
iconColor = if (favorite) Color.Red else Color.Unspecified,
|
||||
modifier = Modifier.onFocusChanged(buttonOnFocusChanged),
|
||||
modifier =
|
||||
Modifier
|
||||
.onFocusChanged(buttonOnFocusChanged)
|
||||
.animateItem(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -132,12 +149,16 @@ fun ExpandablePlayButtons(
|
|||
Duration.ZERO,
|
||||
Icons.Default.MoreVert,
|
||||
{ moreOnClick.invoke() },
|
||||
Modifier.onFocusChanged(buttonOnFocusChanged),
|
||||
Modifier
|
||||
.onFocusChanged(buttonOnFocusChanged)
|
||||
.animateItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val MinButtonSize = 40.dp
|
||||
|
||||
/**
|
||||
* An icon button typically used in a row for playing media
|
||||
*
|
||||
|
|
@ -156,20 +177,36 @@ fun ExpandablePlayButton(
|
|||
val isFocused = interactionSource.collectIsFocusedAsState().value
|
||||
Button(
|
||||
onClick = { onClick.invoke(resume) },
|
||||
modifier = modifier,
|
||||
modifier =
|
||||
modifier.requiredSizeIn(
|
||||
minWidth = MinButtonSize,
|
||||
minHeight = MinButtonSize,
|
||||
maxHeight = MinButtonSize,
|
||||
),
|
||||
contentPadding = DefaultButtonPadding,
|
||||
interactionSource = interactionSource,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 2.dp, top = 2.dp)
|
||||
.height(MinButtonSize),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.ifElse(mirrorIcon, Modifier.graphicsLayer { scaleX = -1f }),
|
||||
modifier =
|
||||
Modifier
|
||||
.size(28.dp)
|
||||
.ifElse(mirrorIcon, Modifier.graphicsLayer { scaleX = -1f }),
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(isFocused) {
|
||||
Spacer(Modifier.size(8.dp))
|
||||
Text(
|
||||
text = stringResource(title),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = Modifier.padding(end = 4.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -190,9 +227,19 @@ fun ExpandableFaButton(
|
|||
val isFocused = interactionSource.collectIsFocusedAsState().value
|
||||
Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier.heightIn(min = 40.dp),
|
||||
modifier =
|
||||
modifier.requiredSizeIn(
|
||||
minWidth = MinButtonSize,
|
||||
minHeight = MinButtonSize,
|
||||
maxHeight = MinButtonSize,
|
||||
),
|
||||
contentPadding = DefaultButtonPadding,
|
||||
interactionSource = interactionSource,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(32.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(iconStringRes),
|
||||
|
|
@ -201,13 +248,14 @@ fun ExpandableFaButton(
|
|||
fontSize = 16.sp,
|
||||
fontFamily = FontAwesome,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(isFocused) {
|
||||
Text(
|
||||
text = stringResource(title),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
modifier = Modifier.padding(end = 4.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -230,3 +278,44 @@ private fun ExpandablePlayButtonsPreview() {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewTvSpec
|
||||
@Composable
|
||||
private fun ViewOptionsPreview() {
|
||||
val source = remember { PreviewInteractionSource() }
|
||||
WholphinTheme {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.padding(16.dp),
|
||||
) {
|
||||
ExpandablePlayButton(
|
||||
title = R.string.play,
|
||||
resume = Duration.ZERO,
|
||||
icon = Icons.Default.PlayArrow,
|
||||
onClick = {},
|
||||
interactionSource = source,
|
||||
)
|
||||
ExpandableFaButton(
|
||||
title = R.string.play,
|
||||
iconStringRes = R.string.fa_eye,
|
||||
onClick = {},
|
||||
modifier = Modifier,
|
||||
interactionSource = source,
|
||||
)
|
||||
|
||||
Row {
|
||||
ExpandableFaButton(
|
||||
title = R.string.mark_unwatched,
|
||||
iconStringRes = R.string.fa_eye,
|
||||
onClick = {},
|
||||
modifier = Modifier,
|
||||
)
|
||||
SortByButton(
|
||||
sortOptions = listOf(),
|
||||
current = SortAndDirection(ItemSortBy.DEFAULT, SortOrder.ASCENDING),
|
||||
onSortChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,13 +13,14 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||
import com.github.damontecres.wholphin.ui.data.flip
|
||||
import com.github.damontecres.wholphin.ui.data.getStringRes
|
||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||
import org.jellyfin.sdk.model.api.SortOrder
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ fun SortByButton(
|
|||
val context = LocalContext.current
|
||||
|
||||
Box(modifier = modifier) {
|
||||
Button(
|
||||
TextButton(
|
||||
onClick = { sortByDropDown = true },
|
||||
onLongClick = {
|
||||
onSortChange.invoke(current.flip())
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
|
|
@ -45,9 +46,9 @@ import androidx.compose.ui.input.key.type
|
|||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.LocalContentColor
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
|
|
@ -56,6 +57,8 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
|||
import com.github.damontecres.wholphin.ui.AppColors
|
||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||
import com.github.damontecres.wholphin.ui.cards.GridCard
|
||||
import com.github.damontecres.wholphin.ui.components.Button
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.playback.isBackwardButton
|
||||
import com.github.damontecres.wholphin.ui.playback.isForwardButton
|
||||
|
|
@ -438,6 +441,7 @@ fun AlphabetButtons(
|
|||
}
|
||||
val focusRequesters = remember { List(letters.length) { FocusRequester() } }
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(4.dp),
|
||||
state = listState,
|
||||
modifier =
|
||||
modifier.focusProperties {
|
||||
|
|
@ -452,7 +456,7 @@ fun AlphabetButtons(
|
|||
) { index ->
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
Button(
|
||||
TextButton(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(24.dp)
|
||||
|
|
@ -472,6 +476,8 @@ fun AlphabetButtons(
|
|||
Text(
|
||||
text = letters[index].toString(),
|
||||
color = color,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ import androidx.compose.ui.unit.Dp
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.ListItem
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
|
|
@ -47,6 +46,7 @@ import androidx.tv.material3.surfaceColorAtElevation
|
|||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.PlaylistInfo
|
||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||
import com.github.damontecres.wholphin.ui.components.Button
|
||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
|
|
|
|||
|
|
@ -28,15 +28,16 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.ui.components.Button
|
||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.seasonEpisode
|
||||
|
|
@ -145,7 +146,7 @@ fun ProgramDialog(
|
|||
.fillMaxWidth(),
|
||||
) {
|
||||
if (now.isAfter(dto.startDate!!) && now.isBefore(dto.endDate!!)) {
|
||||
Button(
|
||||
TextButton(
|
||||
onClick = onWatch,
|
||||
modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -179,7 +180,7 @@ fun ProgramDialog(
|
|||
) {
|
||||
if (dto.isSeries ?: false) {
|
||||
item {
|
||||
Button(
|
||||
TextButton(
|
||||
onClick = {
|
||||
if (isSeriesRecording) {
|
||||
onCancelRecord.invoke(true)
|
||||
|
|
@ -221,7 +222,7 @@ fun ProgramDialog(
|
|||
if (dto.endDate?.isAfter(LocalDateTime.now()) ?: true) {
|
||||
// Only show program specific recording button if it hasn't finished yet
|
||||
item {
|
||||
Button(
|
||||
TextButton(
|
||||
onClick = {
|
||||
if (isRecording) {
|
||||
onCancelRecord.invoke(false)
|
||||
|
|
|
|||
|
|
@ -45,15 +45,13 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.ButtonDefaults
|
||||
import androidx.tv.material3.ClickableSurfaceDefaults
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.ListItem
|
||||
import androidx.tv.material3.LocalContentColor
|
||||
|
|
@ -62,10 +60,14 @@ import androidx.tv.material3.Text
|
|||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.preferences.AppThemeColors
|
||||
import com.github.damontecres.wholphin.ui.AppColors
|
||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||
import com.github.damontecres.wholphin.ui.components.Button
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
import com.github.damontecres.wholphin.ui.seekBack
|
||||
import com.github.damontecres.wholphin.ui.seekForward
|
||||
import com.github.damontecres.wholphin.ui.skipStringRes
|
||||
import com.github.damontecres.wholphin.ui.theme.LocalTheme
|
||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -192,7 +194,8 @@ fun PlaybackControls(
|
|||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
) {
|
||||
currentSegment?.let { segment ->
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = segment.type.skipStringRes,
|
||||
onClick = {
|
||||
playerControls.seekTo(segment.endTicks.ticks.inWholeMilliseconds)
|
||||
},
|
||||
|
|
@ -200,12 +203,8 @@ fun PlaybackControls(
|
|||
Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(end = 32.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(segment.type.skipStringRes),
|
||||
)
|
||||
}
|
||||
}
|
||||
RightPlaybackButtons(
|
||||
captionFocusRequester = captionFocusRequester,
|
||||
settingsFocusRequester = settingsFocusRequester,
|
||||
|
|
@ -286,7 +285,7 @@ fun SeekBar(
|
|||
}
|
||||
}
|
||||
|
||||
private val buttonSpacing = 4.dp
|
||||
private val buttonSpacing = 12.dp
|
||||
|
||||
@Composable
|
||||
fun LeftPlaybackButtons(
|
||||
|
|
@ -435,9 +434,9 @@ fun PlaybackButton(
|
|||
Button(
|
||||
enabled = enabled,
|
||||
onClick = onClick,
|
||||
shape = ButtonDefaults.shape(CircleShape),
|
||||
// shape = ButtonDefaults.shape(CircleShape),
|
||||
colors =
|
||||
ButtonDefaults.colors(
|
||||
ClickableSurfaceDefaults.colors(
|
||||
containerColor = AppColors.TransparentBlack25,
|
||||
focusedContainerColor = selectedColor,
|
||||
),
|
||||
|
|
@ -445,8 +444,7 @@ fun PlaybackButton(
|
|||
interactionSource = interactionSource,
|
||||
modifier =
|
||||
modifier
|
||||
.padding(4.dp)
|
||||
.size(44.dp, 44.dp)
|
||||
.size(36.dp, 36.dp)
|
||||
.onFocusChanged { onControllerInteraction.invoke() },
|
||||
) {
|
||||
Icon(
|
||||
|
|
@ -543,3 +541,17 @@ fun BottomDialog(
|
|||
data class MoreButtonOptions(
|
||||
val options: Map<String, PlaybackAction>,
|
||||
)
|
||||
|
||||
@PreviewTvSpec
|
||||
@Composable
|
||||
private fun ButtonPreview() {
|
||||
WholphinTheme {
|
||||
Row {
|
||||
PlaybackButton(
|
||||
iconRes = R.drawable.baseline_play_arrow_24,
|
||||
onClick = {},
|
||||
onControllerInteraction = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,6 +144,15 @@ fun PlaybackOverlay(
|
|||
} else {
|
||||
null
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(bottom = 8.dp)
|
||||
.onGloballyPositioned {
|
||||
controllerHeight = with(density) { it.size.height.toDp() }
|
||||
},
|
||||
) {
|
||||
Controller(
|
||||
title = item?.title,
|
||||
subtitle = item?.subtitleLong,
|
||||
|
|
@ -170,12 +179,37 @@ fun PlaybackOverlay(
|
|||
},
|
||||
currentSegment = currentSegment,
|
||||
modifier =
|
||||
Modifier
|
||||
Modifier,
|
||||
// Don't use key events because this control has vertical items so up/down is tough to manage
|
||||
.onGloballyPositioned {
|
||||
controllerHeight = with(density) { it.size.height.toDp() }
|
||||
},
|
||||
)
|
||||
when (nextState) {
|
||||
OverlayViewState.CHAPTERS ->
|
||||
Text(
|
||||
text = stringResource(R.string.chapters),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 16.dp, top = 0.dp)
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) state = nextState
|
||||
}.focusable(),
|
||||
)
|
||||
|
||||
OverlayViewState.QUEUE ->
|
||||
Text(
|
||||
text = stringResource(R.string.queue),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 16.dp, top = 0.dp)
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) state = nextState
|
||||
}.focusable(),
|
||||
)
|
||||
|
||||
else -> Spacer(Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(
|
||||
state == OverlayViewState.CHAPTERS,
|
||||
|
|
@ -189,8 +223,8 @@ fun PlaybackOverlay(
|
|||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
.onPreviewKeyEvent { e ->
|
||||
if (e.type == KeyEventType.KeyUp && isUp(e)) {
|
||||
state = OverlayViewState.CONTROLLER
|
||||
|
|
@ -247,7 +281,7 @@ fun PlaybackOverlay(
|
|||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
.padding(bottom = 8.dp)
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) state = OverlayViewState.QUEUE
|
||||
}.focusable(),
|
||||
|
|
@ -269,8 +303,8 @@ fun PlaybackOverlay(
|
|||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
.fillMaxWidth()
|
||||
.onPreviewKeyEvent { e ->
|
||||
if (e.type == KeyEventType.KeyUp && isUp(e)) {
|
||||
if (chapters.isNotEmpty()) {
|
||||
|
|
@ -291,7 +325,7 @@ fun PlaybackOverlay(
|
|||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
|
|
@ -538,32 +572,5 @@ fun Controller(
|
|||
captionFocusRequester = captionFocusRequester,
|
||||
settingsFocusRequester = settingsFocusRequester,
|
||||
)
|
||||
when (nextState) {
|
||||
OverlayViewState.CHAPTERS ->
|
||||
Text(
|
||||
text = stringResource(R.string.chapters),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) onNextStateFocus.invoke()
|
||||
}.focusable(),
|
||||
)
|
||||
|
||||
OverlayViewState.QUEUE ->
|
||||
Text(
|
||||
text = stringResource(R.string.queue),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) onNextStateFocus.invoke()
|
||||
}.focusable(),
|
||||
)
|
||||
|
||||
else -> Spacer(Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ import androidx.compose.ui.graphics.RectangleShape
|
|||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
|
|
@ -57,9 +56,7 @@ import androidx.media3.ui.compose.SURFACE_TYPE_SURFACE_VIEW
|
|||
import androidx.media3.ui.compose.modifiers.resizeWithContentScale
|
||||
import androidx.media3.ui.compose.state.rememberPlayPauseButtonState
|
||||
import androidx.media3.ui.compose.state.rememberPresentationState
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import com.github.damontecres.wholphin.data.model.ItemPlayback
|
||||
import com.github.damontecres.wholphin.data.model.Playlist
|
||||
|
|
@ -71,6 +68,7 @@ import com.github.damontecres.wholphin.ui.LocalImageUrlService
|
|||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.applyToMpv
|
||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.calculateEdgeSize
|
||||
|
|
@ -403,19 +401,16 @@ fun PlaybackPage(
|
|||
delay(10.seconds)
|
||||
segmentCancelled = true
|
||||
}
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = segment.type.skipStringRes,
|
||||
onClick = {
|
||||
segmentCancelled = true
|
||||
player.seekTo(segment.endTicks.ticks.inWholeMilliseconds)
|
||||
},
|
||||
modifier = Modifier.focusRequester(focusRequester),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(segment.type.skipStringRes),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next up episode
|
||||
BackHandler(nextUp != null) {
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ import androidx.compose.ui.unit.Dp
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
|
||||
@Composable
|
||||
fun StringInputDialog(
|
||||
|
|
@ -95,26 +95,20 @@ fun StringInputDialog(
|
|||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.cancel,
|
||||
onClick = onDismissRequest,
|
||||
modifier = Modifier,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.cancel),
|
||||
)
|
||||
}
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.save,
|
||||
onClick = onDone,
|
||||
modifier = Modifier,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.save),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showConfirm) {
|
||||
ConfirmDialog(
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
|
|
@ -54,8 +53,10 @@ import com.github.damontecres.wholphin.services.Release
|
|||
import com.github.damontecres.wholphin.services.UpdateChecker
|
||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||
import com.github.damontecres.wholphin.ui.components.Button
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
import com.github.damontecres.wholphin.ui.dimAndBlur
|
||||
import com.github.damontecres.wholphin.ui.formatBytes
|
||||
import com.github.damontecres.wholphin.ui.setValueOnMain
|
||||
|
|
@ -299,22 +300,16 @@ fun InstallUpdatePageContent(
|
|||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.download_and_update,
|
||||
onClick = onInstallRelease,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.download_and_update),
|
||||
)
|
||||
}
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.cancel,
|
||||
onClick = onCancel,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.cancel),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -27,13 +27,14 @@ import androidx.compose.ui.input.key.type
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||
import com.github.damontecres.wholphin.ui.components.Button
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
import com.github.damontecres.wholphin.ui.playback.isEnterKey
|
||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ fun PinEntry(
|
|||
PinArrowRow(Modifier.align(Alignment.CenterHorizontally))
|
||||
PinEntryDots(input.length, Modifier.align(Alignment.CenterHorizontally))
|
||||
|
||||
Button(
|
||||
TextButton(
|
||||
onClick = onClickServerAuth,
|
||||
modifier =
|
||||
Modifier
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
|
|
@ -37,6 +36,7 @@ import com.github.damontecres.wholphin.R
|
|||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
import com.github.damontecres.wholphin.ui.dimAndBlur
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
|
|
@ -224,7 +224,7 @@ fun SwitchServerContent(
|
|||
|
||||
else -> {}
|
||||
}
|
||||
Button(
|
||||
TextButton(
|
||||
onClick = { submit.invoke() },
|
||||
enabled = url.text.isNotNullOrBlank() && state == LoadingState.Pending,
|
||||
modifier = Modifier,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
|
|
@ -44,6 +43,7 @@ import com.github.damontecres.wholphin.data.model.JellyfinUser
|
|||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||
import com.github.damontecres.wholphin.ui.dimAndBlur
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
|
|
@ -203,16 +203,15 @@ fun SwitchUserContent(
|
|||
)
|
||||
}
|
||||
UserStateError(userState)
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.username_or_password,
|
||||
onClick = {
|
||||
viewModel.cancelQuickConnect()
|
||||
viewModel.clearSwitchUserState()
|
||||
useQuickConnect = false
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(text = "Use username/password")
|
||||
}
|
||||
)
|
||||
} else {
|
||||
val username = rememberTextFieldState()
|
||||
val password = rememberTextFieldState()
|
||||
|
|
@ -282,27 +281,25 @@ fun SwitchUserContent(
|
|||
modifier = Modifier.focusRequester(passwordFocusRequester),
|
||||
)
|
||||
}
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.login,
|
||||
onClick = { onSubmit.invoke() },
|
||||
enabled = username.text.isNotNullOrBlank() && password.text.isNotNullOrBlank(),
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text("Login")
|
||||
}
|
||||
)
|
||||
}
|
||||
if (loginAttempts > 2) {
|
||||
Text(
|
||||
text = "Trouble logging in?",
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
Button(
|
||||
TextButton(
|
||||
stringRes = R.string.show_debug_info,
|
||||
onClick = {
|
||||
viewModel.navigationManager.navigateTo(Destination.Debug)
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text("Show debug info")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,6 +350,8 @@
|
|||
<string name="cast_and_crew"><![CDATA[Cast & Crew]]></string>
|
||||
<string name="guest_stars">Guest stars</string>
|
||||
<string name="edge_size">Edge size</string>
|
||||
<string name="username_or_password">Use username/password</string>
|
||||
<string name="login">Login</string>
|
||||
|
||||
|
||||
<string-array name="theme_song_volume">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue