Эх сурвалжийг харах

Consistent labeled checkbox composable

arkon 1 жил өмнө
parent
commit
c53172265b

+ 11 - 28
app/src/main/java/eu/kanade/presentation/history/components/HistoryDialogs.kt

@@ -1,12 +1,8 @@
 package eu.kanade.presentation.history.components
 
-import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.selection.toggleable
 import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Checkbox
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
@@ -14,12 +10,11 @@ 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.res.stringResource
 import androidx.compose.ui.unit.dp
 import eu.kanade.presentation.theme.TachiyomiTheme
 import eu.kanade.tachiyomi.R
+import tachiyomi.presentation.core.components.LabeledCheckbox
 import tachiyomi.presentation.core.util.ThemePreviews
 
 @Composable
@@ -34,28 +29,16 @@ fun HistoryDeleteDialog(
             Text(text = stringResource(R.string.action_remove))
         },
         text = {
-            Column {
+            Column(
+                verticalArrangement = Arrangement.spacedBy(8.dp),
+            ) {
                 Text(text = stringResource(R.string.dialog_with_checkbox_remove_description))
-                Row(
-                    modifier = Modifier
-                        .padding(top = 16.dp)
-                        .toggleable(
-                            interactionSource = remember { MutableInteractionSource() },
-                            indication = null,
-                            value = removeEverything,
-                            onValueChange = { removeEverything = it },
-                        ),
-                    verticalAlignment = Alignment.CenterVertically,
-                ) {
-                    Checkbox(
-                        checked = removeEverything,
-                        onCheckedChange = null,
-                    )
-                    Text(
-                        modifier = Modifier.padding(start = 4.dp),
-                        text = stringResource(R.string.dialog_with_checkbox_reset),
-                    )
-                }
+
+                LabeledCheckbox(
+                    label = stringResource(R.string.dialog_with_checkbox_reset),
+                    checked = removeEverything,
+                    onCheckedChange = { removeEverything = it },
+                )
             }
         },
         onDismissRequest = onDismissRequest,

+ 13 - 27
app/src/main/java/eu/kanade/presentation/library/DeleteLibraryMangaDialog.kt

@@ -1,11 +1,7 @@
 package eu.kanade.presentation.library
 
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Checkbox
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
@@ -13,11 +9,10 @@ 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.res.stringResource
 import eu.kanade.tachiyomi.R
 import tachiyomi.core.preference.CheckboxState
+import tachiyomi.presentation.core.components.LabeledCheckbox
 
 @Composable
 fun DeleteLibraryMangaDialog(
@@ -62,27 +57,18 @@ fun DeleteLibraryMangaDialog(
         text = {
             Column {
                 list.forEach { state ->
-                    val onCheck = {
-                        val index = list.indexOf(state)
-                        if (index != -1) {
-                            val mutableList = list.toMutableList()
-                            mutableList[index] = state.next() as CheckboxState.State<Int>
-                            list = mutableList.toList()
-                        }
-                    }
-
-                    Row(
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .clickable { onCheck() },
-                        verticalAlignment = Alignment.CenterVertically,
-                    ) {
-                        Checkbox(
-                            checked = state.isChecked,
-                            onCheckedChange = { onCheck() },
-                        )
-                        Text(text = stringResource(state.value))
-                    }
+                    LabeledCheckbox(
+                        label = stringResource(state.value),
+                        checked = state.isChecked,
+                        onCheckedChange = {
+                            val index = list.indexOf(state)
+                            if (index != -1) {
+                                val mutableList = list.toMutableList()
+                                mutableList[index] = state.next() as CheckboxState.State<Int>
+                                list = mutableList.toList()
+                            }
+                        },
+                    )
                 }
             }
         },

+ 7 - 19
app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt

@@ -1,16 +1,12 @@
 package eu.kanade.presentation.manga
 
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Checkbox
 import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
@@ -19,7 +15,6 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
@@ -30,6 +25,7 @@ import eu.kanade.presentation.components.TabbedDialogPaddings
 import eu.kanade.tachiyomi.R
 import tachiyomi.core.preference.TriState
 import tachiyomi.domain.manga.model.Manga
+import tachiyomi.presentation.core.components.LabeledCheckbox
 import tachiyomi.presentation.core.components.RadioItem
 import tachiyomi.presentation.core.components.SortItem
 import tachiyomi.presentation.core.components.TriStateItem
@@ -172,6 +168,7 @@ private fun SetAsDefaultDialog(
     onConfirmed: (optionalChecked: Boolean) -> Unit,
 ) {
     var optionalChecked by rememberSaveable { mutableStateOf(false) }
+
     AlertDialog(
         onDismissRequest = onDismissRequest,
         title = { Text(text = stringResource(R.string.chapter_settings)) },
@@ -181,20 +178,11 @@ private fun SetAsDefaultDialog(
             ) {
                 Text(text = stringResource(R.string.confirm_set_chapter_settings))
 
-                Row(
-                    modifier = Modifier
-                        .clickable { optionalChecked = !optionalChecked }
-                        .padding(vertical = 8.dp)
-                        .fillMaxWidth(),
-                    horizontalArrangement = Arrangement.spacedBy(12.dp),
-                    verticalAlignment = Alignment.CenterVertically,
-                ) {
-                    Checkbox(
-                        checked = optionalChecked,
-                        onCheckedChange = null,
-                    )
-                    Text(text = stringResource(R.string.also_set_chapter_settings_for_library))
-                }
+                LabeledCheckbox(
+                    label = stringResource(R.string.also_set_chapter_settings_for_library),
+                    checked = optionalChecked,
+                    onCheckedChange = { optionalChecked = it },
+                )
             }
         },
         dismissButton = {

+ 12 - 41
app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt

@@ -8,20 +8,13 @@ import android.widget.Toast
 import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.annotation.StringRes
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Checkbox
 import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
@@ -38,7 +31,6 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
 import androidx.core.net.toUri
 import com.hippo.unifile.UniFile
 import eu.kanade.presentation.extensions.RequestStoragePermission
@@ -55,6 +47,7 @@ import eu.kanade.tachiyomi.util.system.copyToClipboard
 import eu.kanade.tachiyomi.util.system.toast
 import kotlinx.coroutines.launch
 import tachiyomi.domain.backup.service.BackupPreferences
+import tachiyomi.presentation.core.components.LabeledCheckbox
 import tachiyomi.presentation.core.components.ScrollbarLazyColumn
 import tachiyomi.presentation.core.util.collectAsState
 import tachiyomi.presentation.core.util.isScrolledToEnd
@@ -160,22 +153,23 @@ object SettingsBackupScreen : SearchableSettings {
                     val state = rememberLazyListState()
                     ScrollbarLazyColumn(state = state) {
                         item {
-                            CreateBackupDialogItem(
-                                isSelected = true,
-                                title = stringResource(R.string.manga),
+                            LabeledCheckbox(
+                                label = stringResource(R.string.manga),
+                                checked = true,
+                                onCheckedChange = {},
                             )
                         }
                         choices.forEach { (k, v) ->
                             item {
                                 val isSelected = flags.contains(k)
-                                CreateBackupDialogItem(
-                                    isSelected = isSelected,
-                                    title = stringResource(v),
-                                    modifier = Modifier.clickable {
-                                        if (isSelected) {
-                                            flags.remove(k)
-                                        } else {
+                                LabeledCheckbox(
+                                    label = stringResource(v),
+                                    checked = isSelected,
+                                    onCheckedChange = {
+                                        if (it) {
                                             flags.add(k)
+                                        } else {
+                                            flags.remove(k)
                                         }
                                     },
                                 )
@@ -204,29 +198,6 @@ object SettingsBackupScreen : SearchableSettings {
         )
     }
 
-    @Composable
-    private fun CreateBackupDialogItem(
-        modifier: Modifier = Modifier,
-        isSelected: Boolean,
-        title: String,
-    ) {
-        Row(
-            verticalAlignment = Alignment.CenterVertically,
-            modifier = modifier.fillMaxWidth(),
-        ) {
-            Checkbox(
-                modifier = Modifier.heightIn(min = 48.dp),
-                checked = isSelected,
-                onCheckedChange = null,
-            )
-            Text(
-                text = title,
-                style = MaterialTheme.typography.bodyMedium.merge(),
-                modifier = Modifier.padding(start = 24.dp),
-            )
-        }
-    }
-
     @Composable
     private fun getRestoreBackupPref(): Preference.PreferenceItem.TextPreference {
         val context = LocalContext.current

+ 12 - 38
app/src/main/java/eu/kanade/presentation/more/settings/widget/MultiSelectListPreferenceWidget.kt

@@ -1,30 +1,20 @@
 package eu.kanade.presentation.more.settings.widget
 
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.selection.selectable
 import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Checkbox
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
-import androidx.compose.material3.minimumInteractiveComponentSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.toMutableStateList
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.DialogProperties
 import eu.kanade.presentation.more.settings.Preference
 import eu.kanade.tachiyomi.R
+import tachiyomi.presentation.core.components.LabeledCheckbox
 
 @Composable
 fun MultiSelectListPreferenceWidget(
@@ -55,33 +45,17 @@ fun MultiSelectListPreferenceWidget(
                     preference.entries.forEach { current ->
                         item {
                             val isSelected = selected.contains(current.key)
-                            val onSelectionChanged = {
-                                when (!isSelected) {
-                                    true -> selected.add(current.key)
-                                    false -> selected.remove(current.key)
-                                }
-                            }
-                            Row(
-                                verticalAlignment = Alignment.CenterVertically,
-                                modifier = Modifier
-                                    .clip(MaterialTheme.shapes.small)
-                                    .selectable(
-                                        selected = isSelected,
-                                        onClick = { onSelectionChanged() },
-                                    )
-                                    .minimumInteractiveComponentSize()
-                                    .fillMaxWidth(),
-                            ) {
-                                Checkbox(
-                                    checked = isSelected,
-                                    onCheckedChange = null,
-                                )
-                                Text(
-                                    text = current.value,
-                                    style = MaterialTheme.typography.bodyMedium,
-                                    modifier = Modifier.padding(start = 24.dp),
-                                )
-                            }
+                            LabeledCheckbox(
+                                label = current.value,
+                                checked = isSelected,
+                                onCheckedChange = {
+                                    if (it) {
+                                        selected.add(current.key)
+                                    } else {
+                                        selected.remove(current.key)
+                                    }
+                                },
+                            )
                         }
                     }
                 }

+ 6 - 15
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt

@@ -1,17 +1,13 @@
 package eu.kanade.tachiyomi.ui.browse.migration.search
 
 import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.FlowRow
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Checkbox
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
@@ -22,7 +18,6 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.toMutableStateList
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
@@ -55,6 +50,7 @@ import tachiyomi.domain.manga.model.MangaUpdate
 import tachiyomi.domain.source.service.SourceManager
 import tachiyomi.domain.track.interactor.GetTracks
 import tachiyomi.domain.track.interactor.InsertTrack
+import tachiyomi.presentation.core.components.LabeledCheckbox
 import tachiyomi.presentation.core.screens.LoadingScreen
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
@@ -92,16 +88,11 @@ internal fun MigrateDialog(
                     modifier = Modifier.verticalScroll(rememberScrollState()),
                 ) {
                     flags.forEachIndexed { index, flag ->
-                        val onChange = { selectedFlags[index] = !selectedFlags[index] }
-                        Row(
-                            modifier = Modifier
-                                .fillMaxWidth()
-                                .clickable(onClick = onChange),
-                            verticalAlignment = Alignment.CenterVertically,
-                        ) {
-                            Checkbox(checked = selectedFlags[index], onCheckedChange = { onChange() })
-                            Text(text = context.getString(flag.titleId))
-                        }
+                        LabeledCheckbox(
+                            label = stringResource(flag.titleId),
+                            checked = selectedFlags[index],
+                            onCheckedChange = { selectedFlags[index] = it },
+                        )
                     }
                 }
             },

+ 46 - 48
app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt

@@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.manga.track
 
 import android.app.Application
 import android.content.Context
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
@@ -13,7 +12,6 @@ import androidx.compose.foundation.layout.windowInsetsPadding
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Delete
 import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.Checkbox
 import androidx.compose.material3.FilledTonalButton
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
@@ -33,6 +31,7 @@ import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
 import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.model.StateScreenModel
 import cafe.adriel.voyager.core.model.coroutineScope
@@ -75,6 +74,7 @@ import tachiyomi.domain.source.service.SourceManager
 import tachiyomi.domain.track.interactor.DeleteTrack
 import tachiyomi.domain.track.interactor.GetTracks
 import tachiyomi.domain.track.model.Track
+import tachiyomi.presentation.core.components.LabeledCheckbox
 import tachiyomi.presentation.core.components.material.AlertDialogContent
 import tachiyomi.presentation.core.components.material.padding
 import uy.kohesive.injekt.Injekt
@@ -94,10 +94,10 @@ data class TrackInfoDialogHomeScreen(
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
         val context = LocalContext.current
-        val sm = rememberScreenModel { Model(mangaId, sourceId) }
+        val screenModel = rememberScreenModel { Model(mangaId, sourceId) }
 
         val dateFormat = remember { UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()) }
-        val state by sm.state.collectAsState()
+        val state by screenModel.state.collectAsState()
 
         TrackInfoDialogHome(
             trackItems = state.trackItems,
@@ -146,7 +146,7 @@ data class TrackInfoDialogHomeScreen(
             },
             onNewSearch = {
                 if (it.tracker is EnhancedTracker) {
-                    sm.registerEnhancedTracking(it)
+                    screenModel.registerEnhancedTracking(it)
                 } else {
                     navigator.push(
                         TrackerSearchScreen(
@@ -261,19 +261,19 @@ private data class TrackStatusSelectorScreen(
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
-        val sm = rememberScreenModel {
+        val screenModel = rememberScreenModel {
             Model(
                 track = track,
                 tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
             )
         }
-        val state by sm.state.collectAsState()
+        val state by screenModel.state.collectAsState()
         TrackStatusSelector(
             selection = state.selection,
-            onSelectionChange = sm::setSelection,
-            selections = remember { sm.getSelections() },
+            onSelectionChange = screenModel::setSelection,
+            selections = remember { screenModel.getSelections() },
             onConfirm = {
-                sm.setStatus()
+                screenModel.setStatus()
                 navigator.pop()
             },
             onDismissRequest = navigator::pop,
@@ -314,20 +314,20 @@ private data class TrackChapterSelectorScreen(
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
-        val sm = rememberScreenModel {
+        val screenModel = rememberScreenModel {
             Model(
                 track = track,
                 tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
             )
         }
-        val state by sm.state.collectAsState()
+        val state by screenModel.state.collectAsState()
 
         TrackChapterSelector(
             selection = state.selection,
-            onSelectionChange = sm::setSelection,
-            range = remember { sm.getRange() },
+            onSelectionChange = screenModel::setSelection,
+            range = remember { screenModel.getRange() },
             onConfirm = {
-                sm.setChapter()
+                screenModel.setChapter()
                 navigator.pop()
             },
             onDismissRequest = navigator::pop,
@@ -373,20 +373,20 @@ private data class TrackScoreSelectorScreen(
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
-        val sm = rememberScreenModel {
+        val screenModel = rememberScreenModel {
             Model(
                 track = track,
                 tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
             )
         }
-        val state by sm.state.collectAsState()
+        val state by screenModel.state.collectAsState()
 
         TrackScoreSelector(
             selection = state.selection,
-            onSelectionChange = sm::setSelection,
-            selections = remember { sm.getSelections() },
+            onSelectionChange = screenModel::setSelection,
+            selections = remember { screenModel.getSelections() },
             onConfirm = {
-                sm.setScore()
+                screenModel.setScore()
                 navigator.pop()
             },
             onDismissRequest = navigator::pop,
@@ -484,7 +484,7 @@ private data class TrackDateSelectorScreen(
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
-        val sm = rememberScreenModel {
+        val screenModel = rememberScreenModel {
             Model(
                 track = track,
                 tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
@@ -503,13 +503,13 @@ private data class TrackDateSelectorScreen(
             } else {
                 stringResource(R.string.track_finished_reading_date)
             },
-            initialSelectedDateMillis = sm.initialSelection,
+            initialSelectedDateMillis = screenModel.initialSelection,
             selectableDates = selectableDates,
             onConfirm = {
-                sm.setDate(it)
+                screenModel.setDate(it)
                 navigator.pop()
             },
-            onRemove = { sm.confirmRemoveDate(navigator) }.takeIf { canRemove },
+            onRemove = { screenModel.confirmRemoveDate(navigator) }.takeIf { canRemove },
             onDismissRequest = navigator::pop,
         )
     }
@@ -557,7 +557,7 @@ private data class TrackDateRemoverScreen(
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
-        val sm = rememberScreenModel {
+        val screenModel = rememberScreenModel {
             Model(
                 track = track,
                 tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
@@ -579,7 +579,7 @@ private data class TrackDateRemoverScreen(
                 )
             },
             text = {
-                val serviceName = sm.getServiceName()
+                val serviceName = screenModel.getServiceName()
                 Text(
                     text = if (start) {
                         stringResource(R.string.track_remove_start_date_conf_text, serviceName)
@@ -598,7 +598,7 @@ private data class TrackDateRemoverScreen(
                     }
                     FilledTonalButton(
                         onClick = {
-                            sm.removeDate()
+                            screenModel.removeDate()
                             navigator.popUntil { it is TrackInfoDialogHomeScreen }
                         },
                         colors = ButtonDefaults.filledTonalButtonColors(
@@ -643,7 +643,7 @@ data class TrackerSearchScreen(
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
-        val sm = rememberScreenModel {
+        val screenModel = rememberScreenModel {
             Model(
                 mangaId = mangaId,
                 currentUrl = currentUrl,
@@ -652,18 +652,18 @@ data class TrackerSearchScreen(
             )
         }
 
-        val state by sm.state.collectAsState()
+        val state by screenModel.state.collectAsState()
 
         var textFieldValue by remember { mutableStateOf(TextFieldValue(initialQuery)) }
         TrackerSearch(
             query = textFieldValue,
             onQueryChange = { textFieldValue = it },
-            onDispatchQuery = { sm.trackingSearch(textFieldValue.text) },
+            onDispatchQuery = { screenModel.trackingSearch(textFieldValue.text) },
             queryResult = state.queryResult,
             selected = state.selected,
-            onSelectedChange = sm::updateSelection,
+            onSelectedChange = screenModel::updateSelection,
             onConfirmSelection = {
-                sm.registerTracking(state.selected!!)
+                screenModel.registerTracking(state.selected!!)
                 navigator.pop()
             },
             onDismissRequest = navigator::pop,
@@ -731,14 +731,14 @@ private data class TrackerRemoveScreen(
     @Composable
     override fun Content() {
         val navigator = LocalNavigator.currentOrThrow
-        val sm = rememberScreenModel {
+        val screenModel = rememberScreenModel {
             Model(
                 mangaId = mangaId,
                 track = track,
                 tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
             )
         }
-        val serviceName = sm.getName()
+        val serviceName = screenModel.getName()
         var removeRemoteTrack by remember { mutableStateOf(false) }
         AlertDialogContent(
             modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars),
@@ -755,21 +755,19 @@ private data class TrackerRemoveScreen(
                 )
             },
             text = {
-                Column {
+                Column(
+                    verticalArrangement = Arrangement.spacedBy(8.dp),
+                ) {
                     Text(
                         text = stringResource(R.string.track_delete_text, serviceName),
                     )
-                    if (sm.isDeletable()) {
-                        val onChange = { removeRemoteTrack = !removeRemoteTrack }
-                        Row(
-                            modifier = Modifier
-                                .fillMaxWidth()
-                                .clickable(onClick = onChange),
-                            verticalAlignment = Alignment.CenterVertically,
-                        ) {
-                            Checkbox(checked = removeRemoteTrack, onCheckedChange = { onChange() })
-                            Text(text = stringResource(R.string.track_delete_remote_text, serviceName))
-                        }
+
+                    if (screenModel.isDeletable()) {
+                        LabeledCheckbox(
+                            label = stringResource(R.string.track_delete_remote_text, serviceName),
+                            checked = removeRemoteTrack,
+                            onCheckedChange = { removeRemoteTrack = it },
+                        )
                     }
                 }
             },
@@ -786,8 +784,8 @@ private data class TrackerRemoveScreen(
                     }
                     FilledTonalButton(
                         onClick = {
-                            sm.unregisterTracking(serviceId)
-                            if (removeRemoteTrack) sm.deleteMangaFromService()
+                            screenModel.unregisterTracking(serviceId)
+                            if (removeRemoteTrack) screenModel.deleteMangaFromService()
                             navigator.pop()
                         },
                         colors = ButtonDefaults.filledTonalButtonColors(

+ 44 - 0
presentation-core/src/main/java/tachiyomi/presentation/core/components/LabeledCheckbox.kt

@@ -0,0 +1,44 @@
+package tachiyomi.presentation.core.components
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun LabeledCheckbox(
+    modifier: Modifier = Modifier,
+    label: String,
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+) {
+    Row(
+        modifier = modifier
+            .clip(MaterialTheme.shapes.small)
+            .fillMaxWidth()
+            .heightIn(min = 48.dp)
+            .clickable(
+                role = Role.Checkbox,
+                onClick = { onCheckedChange(!checked) },
+            ),
+        verticalAlignment = Alignment.CenterVertically,
+        horizontalArrangement = Arrangement.spacedBy(8.dp),
+    ) {
+        Checkbox(
+            checked = checked,
+            onCheckedChange = null,
+        )
+
+        Text(text = label)
+    }
+}