11package com.capyreader.app.ui.settings.panels
22
3+ import androidx.compose.foundation.background
4+ import androidx.compose.foundation.border
5+ import androidx.compose.foundation.layout.Arrangement
6+ import androidx.compose.foundation.layout.Box
37import androidx.compose.foundation.layout.Column
8+ import androidx.compose.foundation.layout.Row
9+ import androidx.compose.foundation.layout.Spacer
10+ import androidx.compose.foundation.layout.aspectRatio
11+ import androidx.compose.foundation.layout.fillMaxWidth
412import androidx.compose.foundation.layout.padding
13+ import androidx.compose.foundation.layout.size
14+ import androidx.compose.foundation.layout.width
15+ import androidx.compose.material3.Icon
16+ import androidx.compose.material3.ListItemDefaults
17+ import androidx.compose.material3.MaterialTheme
518import androidx.compose.material3.Slider
19+ import androidx.compose.material3.Text
620import androidx.compose.runtime.Composable
721import androidx.compose.runtime.Immutable
22+ import androidx.compose.ui.Alignment
823import androidx.compose.ui.Modifier
24+ import androidx.compose.ui.draw.clip
25+ import androidx.compose.ui.graphics.Shape
26+ import androidx.compose.ui.res.painterResource
927import androidx.compose.ui.res.stringResource
28+ import androidx.compose.ui.text.font.FontWeight
29+ import androidx.compose.ui.text.style.TextOverflow
1030import androidx.compose.ui.tooling.preview.Preview
1131import androidx.compose.ui.unit.dp
1232import com.capyreader.app.R
1333import com.capyreader.app.common.ImagePreview
1434import com.capyreader.app.common.RowItem
35+ import com.capyreader.app.preferences.AppTheme
1536import com.capyreader.app.ui.articles.ArticleListFontScale
37+ import com.capyreader.app.ui.articles.ArticleRowOptions
38+ import com.capyreader.app.ui.articles.FaviconBadge
39+ import com.capyreader.app.ui.articles.StyleProviders
40+ import com.capyreader.app.ui.articles.list.ArticleListItem
1641import com.capyreader.app.ui.components.FormSection
1742import com.capyreader.app.ui.components.LabelStyle
1843import com.capyreader.app.ui.components.TextSwitch
1944import com.capyreader.app.ui.settings.PreferenceSelect
45+ import com.capyreader.app.ui.theme.LocalAppTheme
2046import kotlin.math.roundToInt
2147
2248@Immutable
@@ -42,6 +68,24 @@ fun ArticleListSettings(
4268 val fontScales = ArticleListFontScale .entries
4369
4470 Column {
71+ PreviewArticleRow (options = options)
72+
73+ FormSection (
74+ title = stringResource(R .string.article_font_scale_label),
75+ labelStyle = LabelStyle .COMPACT ,
76+ ) {
77+ RowItem {
78+ Slider (
79+ steps = fontScales.size - 2 ,
80+ valueRange = 0f .. (fontScales.size - 1 ).toFloat(),
81+ value = options.fontScale.ordinal.toFloat(),
82+ onValueChange = {
83+ options.updateFontScale(fontScales[it.roundToInt()])
84+ }
85+ )
86+ }
87+ }
88+
4589 RowItem {
4690 TextSwitch (
4791 onCheckedChange = options.updateFeedName,
@@ -75,26 +119,155 @@ fun ArticleListSettings(
75119 stringResource(id = it.translationKey)
76120 }
77121 )
122+ }
123+ }
78124
79- FormSection (
80- modifier = Modifier .padding(top = 16 .dp),
81- title = stringResource(R .string.article_font_scale_label),
82- labelStyle = LabelStyle .COMPACT ,
125+ @Composable
126+ private fun PreviewArticleRow (options : ArticleListOptions ) {
127+ val rowOptions = ArticleRowOptions (
128+ showIcon = options.showFeedIcons,
129+ showSummary = options.showSummary,
130+ showFeedName = options.showFeedName,
131+ imagePreview = options.imagePreview,
132+ fontScale = options.fontScale,
133+ shortenTitles = options.shortenTitles,
134+ dim = false ,
135+ )
136+ val colors = ListItemDefaults .colors()
137+ val overlineColor = colors.overlineContentColor
138+
139+ StyleProviders (options = rowOptions) {
140+ Column (
141+ modifier = Modifier
142+ .padding(16 .dp)
143+ .border(
144+ width = 1 .dp,
145+ color = MaterialTheme .colorScheme.outlineVariant,
146+ shape = MaterialTheme .shapes.medium,
147+ )
83148 ) {
84- RowItem {
85- Slider (
86- steps = fontScales.size - 2 ,
87- valueRange = 0f .. (fontScales.size - 1 ).toFloat(),
88- value = options.fontScale.ordinal.toFloat(),
89- onValueChange = {
90- options.updateFontScale(fontScales[it.roundToInt()])
91- }
149+ ArticleListItem (
150+ headlineContent = {
151+ Text (
152+ text = PREVIEW_TITLE ,
153+ maxLines = if (options.shortenTitles) 3 else Int .MAX_VALUE ,
154+ overflow = TextOverflow .Ellipsis ,
155+ fontWeight = FontWeight .Bold ,
92156 )
93- }
157+ },
158+ overlineContent = {
159+ Row (
160+ horizontalArrangement = Arrangement .SpaceBetween ,
161+ verticalAlignment = Alignment .CenterVertically ,
162+ modifier = Modifier
163+ .fillMaxWidth()
164+ .padding(bottom = 2 .dp)
165+ ) {
166+ if (options.showFeedName) {
167+ Text (
168+ text = PREVIEW_FEED_NAME ,
169+ color = overlineColor,
170+ maxLines = 1 ,
171+ overflow = TextOverflow .Ellipsis ,
172+ modifier = Modifier .weight(1f )
173+ )
174+ Spacer (Modifier .width(16 .dp))
175+ }
176+ Text (
177+ text = PREVIEW_TIME ,
178+ color = overlineColor,
179+ maxLines = 1 ,
180+ )
181+ }
182+ },
183+ supportingContent = if (options.showSummary || options.imagePreview == ImagePreview .LARGE ) {
184+ {
185+ Column (
186+ verticalArrangement = Arrangement .spacedBy(4 .dp),
187+ modifier = Modifier .padding(vertical = 4 .dp),
188+ ) {
189+ if (options.showSummary) {
190+ Text (
191+ text = PREVIEW_SUMMARY ,
192+ maxLines = 2 ,
193+ overflow = TextOverflow .Ellipsis ,
194+ )
195+ }
196+ if (options.imagePreview == ImagePreview .LARGE ) {
197+ PreviewImage (imagePreview = options.imagePreview)
198+ }
199+ }
200+ }
201+ } else {
202+ null
203+ },
204+ leadingContent = if (options.showFeedIcons) {
205+ { FaviconBadge (url = null ) }
206+ } else {
207+ null
208+ },
209+ trailingContent = if (options.imagePreview.showInline()) {
210+ { PreviewImage (imagePreview = options.imagePreview) }
211+ } else {
212+ null
213+ },
214+ )
94215 }
95216 }
96217}
97218
219+ @Composable
220+ private fun PreviewImage (imagePreview : ImagePreview ) {
221+ val sizeModifier = when (imagePreview) {
222+ ImagePreview .SMALL -> Modifier .size(56 .dp)
223+ ImagePreview .MEDIUM -> Modifier .size(84 .dp)
224+ else -> Modifier .fillMaxWidth().aspectRatio(3 / 2f )
225+ }
226+
227+ val shape = MaterialTheme .shapes.small
228+
229+ Box (
230+ contentAlignment = Alignment .Center ,
231+ modifier = sizeModifier
232+ .monochromeBorder(shape)
233+ .clip(shape)
234+ .background(MaterialTheme .colorScheme.surfaceContainer)
235+ ) {
236+ Icon (
237+ painter = painterResource(R .drawable.icon_empty_list),
238+ contentDescription = null ,
239+ tint = MaterialTheme .colorScheme.onSurfaceVariant.copy(alpha = 0.6f ),
240+ modifier = Modifier .size(
241+ when (imagePreview) {
242+ ImagePreview .SMALL -> 48 .dp
243+ ImagePreview .MEDIUM -> 64 .dp
244+ else -> 80 .dp
245+ }
246+ )
247+ )
248+ }
249+ }
250+
251+ @Composable
252+ private fun Modifier.monochromeBorder (shape : Shape ): Modifier {
253+ val isMonochrome = LocalAppTheme .current.value == AppTheme .MONOCHROME
254+
255+ return if (isMonochrome) {
256+ border(
257+ width = 1 .dp,
258+ color = MaterialTheme .colorScheme.outline,
259+ shape = shape,
260+ )
261+ } else {
262+ this
263+ }
264+ }
265+
266+ private const val PREVIEW_TITLE = " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua"
267+ private const val PREVIEW_FEED_NAME = " Lorem Ipsum"
268+ private const val PREVIEW_TIME = " 3h"
269+ private const val PREVIEW_SUMMARY = " Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam."
270+
98271@Preview
99272@Composable
100273private fun ArticleListSettingsPreview () {
0 commit comments