Skip to content

Commit 5e427f7

Browse files
committed
refactor(ui): merge CheckableItem & ConfigLayout
1 parent 39a9887 commit 5e427f7

15 files changed

Lines changed: 328 additions & 297 deletions

File tree

app/src/main/kotlin/com/w2sv/wifiwidget/ui/designsystem/Disclaiming.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,24 @@ import com.w2sv.wifiwidget.ui.theme.onSurfaceVariantLowAlpha
1414
import com.w2sv.wifiwidget.ui.util.WithLocalContentColor
1515

1616
@Composable
17-
fun DisclaimerRow(text: String, modifier: Modifier = Modifier) {
17+
fun Disclaimer(text: String, modifier: Modifier = Modifier) {
1818
Row(
1919
verticalAlignment = Alignment.CenterVertically,
2020
modifier = modifier,
2121
horizontalArrangement = Arrangement.spacedBy(16.dp)
2222
) {
2323
WithLocalContentColor(colorScheme.onSurfaceVariantLowAlpha) {
2424
InfoIcon()
25-
Text(text = text, style = typography.explanation)
25+
ExplanationText(text)
2626
}
2727
}
2828
}
29+
30+
@Composable
31+
fun ExplanationText(text: String, modifier: Modifier = Modifier) {
32+
Text(
33+
text = text,
34+
style = typography.explanation,
35+
modifier = modifier
36+
)
37+
}

app/src/main/kotlin/com/w2sv/wifiwidget/ui/designsystem/DynamicColors.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fun ConfigureUseDynamicColors(
1616
below: BoxScopeComposable? = null
1717
) {
1818
TLayout(
19-
label = {
19+
central = {
2020
Text(
2121
text = stringResource(R.string.dynamic_colors),
2222
color = MaterialTheme.colorScheme.onSurface

app/src/main/kotlin/com/w2sv/wifiwidget/ui/designsystem/TLayout.kt

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.w2sv.wifiwidget.ui.designsystem
22

3+
import androidx.annotation.FloatRange
34
import androidx.compose.foundation.background
45
import androidx.compose.foundation.layout.Box
56
import androidx.compose.foundation.layout.BoxScope
@@ -18,7 +19,9 @@ import androidx.compose.ui.res.stringResource
1819
import androidx.compose.ui.tooling.preview.Preview
1920
import androidx.compose.ui.unit.Dp
2021
import androidx.compose.ui.unit.dp
22+
import androidx.constraintlayout.compose.ConstrainScope
2123
import androidx.constraintlayout.compose.ConstraintLayout
24+
import androidx.constraintlayout.compose.ConstraintLayoutBaseScope
2225
import androidx.constraintlayout.compose.Dimension
2326
import com.w2sv.core.common.R
2427
import com.w2sv.wifiwidget.ui.theme.AppTheme
@@ -37,64 +40,67 @@ data class Margins(val start: Dp = 0.dp, val top: Dp = 0.dp, val end: Dp = 0.dp,
3740
* End-anchoring applied to the below slot.
3841
*/
3942
enum class BelowEndAnchoring {
40-
LabelEnd,
43+
CentralEnd,
4144
ParentEnd;
4245

4346
companion object {
44-
val Default get() = LabelEnd
47+
val Default get() = CentralEnd
4548
}
4649
}
4750

4851
@Composable
4952
fun TLayout(
50-
label: BoxScopeComposable,
53+
central: BoxScopeComposable,
5154
modifier: Modifier = Modifier,
5255
leading: BoxScopeComposable? = null,
5356
trailing: BoxScopeComposable? = null,
5457
below: BoxScopeComposable? = null,
55-
labelMargins: Margins = Margins(start = 16.dp),
56-
belowMargins: Margins = Margins(top = 2.dp),
58+
centralMargins: Margins = Margins.empty,
59+
belowMargins: Margins = Margins.empty,
5760
belowEndAnchoring: BelowEndAnchoring = BelowEndAnchoring.Default
5861
) {
5962
ConstraintLayout(modifier = modifier) {
60-
val (leadingRef, labelRef, belowRef, trailingRef) = createRefs()
63+
val (leadingRef, centralRef, belowRef, trailingRef) = createRefs()
6164

6265
val hasLeading = leading != null
6366
val hasTrailing = trailing != null
64-
val hasBelow = below != null
67+
68+
val topRowTop = createTopBarrier(leadingRef, centralRef, trailingRef)
69+
val topRowBottom = createBottomBarrier(leadingRef, centralRef, trailingRef)
6570

6671
leading?.let {
6772
Box(
6873
modifier = Modifier.constrainAs(leadingRef) {
6974
start.linkTo(parent.start)
70-
centerVerticallyTo(labelRef)
75+
linkTo(top = topRowTop, bottom = topRowBottom)
7176
},
7277
content = it
7378
)
7479
}
7580

7681
Box(
77-
modifier = Modifier.constrainAs(labelRef) {
78-
linkTo(
79-
top = parent.top,
80-
bottom = if (hasBelow) belowRef.top else parent.bottom
81-
)
82+
modifier = Modifier.constrainAs(centralRef) {
8283
linkTo(
84+
top = topRowTop,
85+
bottom = topRowBottom,
8386
start = if (hasLeading) leadingRef.end else parent.start,
8487
end = if (hasTrailing) trailingRef.start else parent.end,
85-
startMargin = labelMargins.start
88+
margins = centralMargins
8689
)
8790
width = Dimension.fillToConstraints
8891
},
89-
content = label
92+
content = central
9093
)
9194

9295
trailing?.let {
9396
Box(
9497
modifier = Modifier.constrainAs(trailingRef) {
95-
start.linkTo(labelRef.end)
96-
end.linkTo(parent.end)
97-
centerVerticallyTo(labelRef)
98+
linkTo(
99+
top = topRowTop,
100+
bottom = topRowBottom,
101+
start = centralRef.end,
102+
end = parent.end
103+
)
98104
},
99105
content = it
100106
)
@@ -103,13 +109,15 @@ fun TLayout(
103109
below?.let {
104110
Box(
105111
modifier = Modifier.constrainAs(belowRef) {
106-
top.linkTo(labelRef.bottom, margin = belowMargins.top)
107112
linkTo(
108-
start = labelRef.start,
113+
start = centralRef.start,
109114
end = when (belowEndAnchoring) {
110-
BelowEndAnchoring.LabelEnd -> labelRef.end
115+
BelowEndAnchoring.CentralEnd -> centralRef.end
111116
BelowEndAnchoring.ParentEnd -> parent.end
112-
}
117+
},
118+
top = centralRef.bottom,
119+
bottom = parent.bottom,
120+
margins = belowMargins
113121
)
114122
width = Dimension.fillToConstraints
115123
},
@@ -119,14 +127,44 @@ fun TLayout(
119127
}
120128
}
121129

130+
private fun ConstrainScope.linkTo(
131+
start: ConstraintLayoutBaseScope.VerticalAnchor,
132+
top: ConstraintLayoutBaseScope.HorizontalAnchor,
133+
end: ConstraintLayoutBaseScope.VerticalAnchor,
134+
bottom: ConstraintLayoutBaseScope.HorizontalAnchor,
135+
margins: Margins,
136+
goneMargins: Margins = Margins.empty,
137+
@FloatRange(from = 0.0, to = 1.0) horizontalBias: Float = 0.5f,
138+
@FloatRange(from = 0.0, to = 1.0) verticalBias: Float = 0.5f
139+
) {
140+
linkTo(
141+
start = start,
142+
end = end,
143+
startMargin = margins.start,
144+
endMargin = margins.end,
145+
startGoneMargin = goneMargins.start,
146+
endGoneMargin = goneMargins.end,
147+
bias = horizontalBias
148+
)
149+
linkTo(
150+
top = top,
151+
bottom = bottom,
152+
topMargin = margins.top,
153+
bottomMargin = margins.bottom,
154+
topGoneMargin = goneMargins.top,
155+
bottomGoneMargin = goneMargins.bottom,
156+
bias = verticalBias
157+
)
158+
}
159+
122160
@Preview
123161
@Composable
124162
private fun Complete() {
125163
AppTheme {
126164
Surface {
127165
TLayout(
128166
modifier = Modifier.fillMaxWidth(),
129-
label = {
167+
central = {
130168
Text(
131169
text = stringResource(R.string.dynamic_colors),
132170
color = MaterialTheme.colorScheme.onSurface
@@ -157,7 +195,7 @@ private fun BelowEndAnchoredToParent() {
157195
Surface {
158196
TLayout(
159197
modifier = Modifier.fillMaxWidth(),
160-
label = {
198+
central = {
161199
Text(
162200
text = stringResource(R.string.dynamic_colors),
163201
color = MaterialTheme.colorScheme.onSurface
@@ -192,7 +230,7 @@ private fun WithoutTrailing() {
192230
Surface {
193231
TLayout(
194232
modifier = Modifier.fillMaxWidth(),
195-
label = {
233+
central = {
196234
Text(
197235
text = stringResource(R.string.dynamic_colors),
198236
color = MaterialTheme.colorScheme.onSurface
@@ -217,7 +255,7 @@ private fun WithoutLeading() {
217255
Surface {
218256
TLayout(
219257
modifier = Modifier.fillMaxWidth(),
220-
label = {
258+
central = {
221259
Text(
222260
text = stringResource(R.string.dynamic_colors),
223261
color = MaterialTheme.colorScheme.onSurface
@@ -247,7 +285,7 @@ private fun WithoutLeadingAndBelow() {
247285
Surface {
248286
TLayout(
249287
modifier = Modifier.fillMaxWidth(),
250-
label = {
288+
central = {
251289
Text(
252290
text = stringResource(R.string.dynamic_colors),
253291
color = MaterialTheme.colorScheme.onSurface
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package com.w2sv.wifiwidget.ui.designsystem.configlist
2+
3+
import androidx.compose.foundation.layout.Row
4+
import androidx.compose.foundation.layout.RowScope
5+
import androidx.compose.foundation.layout.Spacer
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.foundation.layout.size
9+
import androidx.compose.foundation.layout.width
10+
import androidx.compose.material3.Checkbox
11+
import androidx.compose.material3.IconButton
12+
import androidx.compose.material3.MaterialTheme.colorScheme
13+
import androidx.compose.material3.Text
14+
import androidx.compose.runtime.Composable
15+
import androidx.compose.ui.Alignment
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.res.stringResource
18+
import androidx.compose.ui.unit.TextUnit
19+
import androidx.compose.ui.unit.dp
20+
import com.w2sv.composed.core.extensions.thenIfNotNull
21+
import com.w2sv.core.common.R
22+
import com.w2sv.wifiwidget.ui.designsystem.BelowEndAnchoring
23+
import com.w2sv.wifiwidget.ui.designsystem.ExplanationText
24+
import com.w2sv.wifiwidget.ui.designsystem.IconDefaults
25+
import com.w2sv.wifiwidget.ui.designsystem.InfoIcon
26+
import com.w2sv.wifiwidget.ui.designsystem.Margins
27+
import com.w2sv.wifiwidget.ui.designsystem.TLayout
28+
import com.w2sv.wifiwidget.ui.util.VerticallyAnimatedVisibility
29+
import com.w2sv.wifiwidget.ui.util.contentDescription
30+
import com.w2sv.wifiwidget.ui.util.orAlphaDecreasedIf
31+
import com.w2sv.wifiwidget.ui.util.shake
32+
33+
@Composable
34+
fun ActionableConfigItem(
35+
item: ConfigItem.Actionable,
36+
modifier: Modifier = Modifier,
37+
fontSize: TextUnit = TextUnit.Unspecified,
38+
padStartIfNoToggleButton: Boolean = false
39+
) {
40+
val expandableListState = item.contentBeneath?.asSubSettingsOrNull?.run {
41+
rememberExpandableListState(
42+
isPropertyEnabled = item.isEnabled(),
43+
allowCollapsing = allowCollapsing
44+
)
45+
}
46+
val hasSubSettings = expandableListState != null
47+
48+
TLayout(
49+
central = {
50+
Text(
51+
text = stringResource(id = item.property.labelRes),
52+
fontSize = fontSize,
53+
color = colorScheme.onBackground.orAlphaDecreasedIf(!item.isEnabled())
54+
)
55+
},
56+
modifier = modifier
57+
.fillMaxWidth()
58+
.then(item.modifier)
59+
.thenIfNotNull(item.shakeController) { shake(it) },
60+
below = item.contentBeneath?.let { content ->
61+
{
62+
ContentBeneath(
63+
content = content,
64+
expandSubSettings = { expandableListState?.isExpanded == true }
65+
)
66+
}
67+
},
68+
belowEndAnchoring = if (hasSubSettings) BelowEndAnchoring.ParentEnd else BelowEndAnchoring.CentralEnd,
69+
belowMargins = if (hasSubSettings) {
70+
Margins(
71+
top = ConfigListToken.subSettingsTopMargin,
72+
start = (-8).dp // related to ConfigListToken.subSettingsStartPadding, but slightly smaller on purpose
73+
)
74+
} else {
75+
Margins.empty
76+
},
77+
leading = {
78+
expandableListState?.run {
79+
if (allowCollapsing) {
80+
ExpandCollapseButton(
81+
expand = isExpanded,
82+
onClick = ::toggle,
83+
isEnabled = item.isEnabled()
84+
)
85+
}
86+
} ?: run {
87+
if (padStartIfNoToggleButton) {
88+
Spacer(modifier = Modifier.width(ConfigListToken.expandCollapseButtonWidth))
89+
}
90+
}
91+
},
92+
trailing = { Trailing(item) }
93+
)
94+
}
95+
96+
@Composable
97+
private fun Trailing(item: ConfigItem.Actionable) {
98+
Row(verticalAlignment = Alignment.CenterVertically) {
99+
when (item) {
100+
is ConfigItem.Checkable -> CheckableTrailingContent(item)
101+
is ConfigItem.WithCustomTrailing -> item.trailing(this)
102+
}
103+
}
104+
}
105+
106+
@Composable
107+
private fun RowScope.CheckableTrailingContent(item: ConfigItem.Checkable) {
108+
val label = stringResource(id = item.property.labelRes)
109+
110+
item.showInfoDialog?.let {
111+
InfoIconButton(
112+
onClick = it,
113+
contentDescription = stringResource(id = R.string.info_icon_cd, label)
114+
)
115+
}
116+
Checkbox(
117+
checked = item.isChecked(),
118+
onCheckedChange = { item.onCheckedChange(it) },
119+
modifier = Modifier.contentDescription(
120+
stringResource(
121+
id = if (item.isChecked()) R.string.disable_arg else R.string.enable_arg,
122+
label
123+
)
124+
)
125+
)
126+
}
127+
128+
@Composable
129+
private fun ContentBeneath(content: ConfigItem.ContentBeneath, expandSubSettings: () -> Boolean) {
130+
when (content) {
131+
is ConfigItem.Explanation -> ExplanationText(text = stringResource(content.stringRes))
132+
133+
is ConfigItem.SubSettings -> VerticallyAnimatedVisibility(visible = expandSubSettings()) {
134+
SubSettings(elements = content.elements, modifier = Modifier.padding(bottom = ConfigListToken.subSettingsBottomMargin))
135+
}
136+
}
137+
}
138+
139+
@Composable
140+
private fun InfoIconButton(
141+
onClick: () -> Unit,
142+
contentDescription: String,
143+
modifier: Modifier = Modifier
144+
) {
145+
IconButton(onClick = onClick, modifier = modifier) {
146+
InfoIcon(
147+
contentDescription = contentDescription,
148+
modifier = Modifier.size(IconDefaults.SizeBig),
149+
tint = colorScheme.onSurfaceVariant
150+
)
151+
}
152+
}

0 commit comments

Comments
 (0)