Skip to content

Commit 5e29017

Browse files
xororzclaude
andcommitted
feat: checkbox-style reproduce dialog with select-all toggle
Replace the seed yes/no confirmation with a per-field checkbox dialog so users can pick exactly which parameters to copy back to the prompt page, and add a select-all/deselect-all toggle to both the share and reproduce dialogs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 00c1450 commit 5e29017

5 files changed

Lines changed: 192 additions & 96 deletions

File tree

app/src/main/java/io/github/xororz/localdream/ui/components/ParamShareDialogs.kt

Lines changed: 127 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import androidx.compose.ui.text.font.FontWeight
3131
import androidx.compose.ui.unit.Dp
3232
import androidx.compose.ui.unit.dp
3333
import io.github.xororz.localdream.R
34+
import io.github.xororz.localdream.data.GenerationMode
35+
import io.github.xororz.localdream.ui.screens.GenerationParameters
3436
import io.github.xororz.localdream.utils.ImportedParams
3537
import io.github.xororz.localdream.utils.ParamShareField
3638
import io.github.xororz.localdream.utils.schedulerDisplayName
@@ -164,18 +166,136 @@ fun ShareParametersDialog(
164166
}
165167
},
166168
confirmButton = {
167-
TextButton(
168-
onClick = { onConfirm(selected.value, useBase64) },
169-
enabled = selected.value.isNotEmpty()
169+
Row(
170+
modifier = Modifier.fillMaxWidth(),
171+
horizontalArrangement = Arrangement.SpaceBetween,
172+
verticalAlignment = Alignment.CenterVertically,
170173
) {
171-
Text(stringResource(R.string.share_copy_to_clipboard))
174+
val allSelected = selected.value.size == availableFields.size
175+
TextButton(
176+
onClick = {
177+
selected.value =
178+
if (allSelected) emptySet() else availableFields.toSet()
179+
}
180+
) {
181+
Text(
182+
stringResource(
183+
if (allSelected) R.string.deselect_all else R.string.select_all
184+
)
185+
)
186+
}
187+
Row {
188+
TextButton(onClick = onDismiss) {
189+
Text(stringResource(R.string.cancel))
190+
}
191+
TextButton(
192+
onClick = { onConfirm(selected.value, useBase64) },
193+
enabled = selected.value.isNotEmpty()
194+
) {
195+
Text(stringResource(R.string.share_copy_to_clipboard))
196+
}
197+
}
172198
}
173199
},
174-
dismissButton = {
175-
TextButton(onClick = onDismiss) {
176-
Text(stringResource(R.string.cancel))
200+
)
201+
}
202+
203+
@Composable
204+
fun ReproduceParametersDialog(
205+
params: GenerationParameters,
206+
onApply: (selected: Set<ParamShareField>) -> Unit,
207+
onDismiss: () -> Unit,
208+
) {
209+
val available = remember(params) {
210+
buildList {
211+
add(ParamShareField.PROMPT)
212+
add(ParamShareField.NEGATIVE_PROMPT)
213+
add(ParamShareField.STEPS)
214+
add(ParamShareField.CFG)
215+
if (params.seed != null) add(ParamShareField.SEED)
216+
add(ParamShareField.SCHEDULER)
217+
if (params.mode != GenerationMode.TXT2IMG) {
218+
add(ParamShareField.DENOISE_STRENGTH)
177219
}
178220
}
221+
}
222+
val selected = remember(params) {
223+
mutableStateOf<Set<ParamShareField>>(available.toSet())
224+
}
225+
226+
AlertDialog(
227+
onDismissRequest = onDismiss,
228+
title = { Text(stringResource(R.string.reproduce_params_title)) },
229+
text = {
230+
Column(
231+
modifier = Modifier.verticalScroll(rememberScrollState()),
232+
verticalArrangement = Arrangement.spacedBy(2.dp)
233+
) {
234+
Text(
235+
stringResource(R.string.reproduce_params_hint),
236+
style = MaterialTheme.typography.bodySmall,
237+
color = MaterialTheme.colorScheme.onSurfaceVariant
238+
)
239+
available.forEach { field ->
240+
val preview = when (field) {
241+
ParamShareField.PROMPT -> params.prompt
242+
ParamShareField.NEGATIVE_PROMPT -> params.negativePrompt
243+
ParamShareField.STEPS -> params.steps.toString()
244+
ParamShareField.CFG -> "%.1f".format(params.cfg)
245+
ParamShareField.SEED -> params.seed?.toString()
246+
ParamShareField.SCHEDULER -> schedulerDisplayName(params.scheduler)
247+
ParamShareField.DENOISE_STRENGTH ->
248+
"%.2f".format(params.denoiseStrength)
249+
250+
ParamShareField.MODE -> null
251+
}
252+
FieldRow(
253+
field = field,
254+
checked = field in selected.value,
255+
preview = preview,
256+
onToggle = {
257+
selected.value = if (field in selected.value) {
258+
selected.value - field
259+
} else {
260+
selected.value + field
261+
}
262+
}
263+
)
264+
}
265+
}
266+
},
267+
confirmButton = {
268+
Row(
269+
modifier = Modifier.fillMaxWidth(),
270+
horizontalArrangement = Arrangement.SpaceBetween,
271+
verticalAlignment = Alignment.CenterVertically,
272+
) {
273+
val allSelected = selected.value.size == available.size
274+
TextButton(
275+
onClick = {
276+
selected.value =
277+
if (allSelected) emptySet() else available.toSet()
278+
}
279+
) {
280+
Text(
281+
stringResource(
282+
if (allSelected) R.string.deselect_all else R.string.select_all
283+
)
284+
)
285+
}
286+
Row {
287+
TextButton(onClick = onDismiss) {
288+
Text(stringResource(R.string.cancel))
289+
}
290+
TextButton(
291+
onClick = { onApply(selected.value) },
292+
enabled = selected.value.isNotEmpty()
293+
) {
294+
Text(stringResource(R.string.import_apply))
295+
}
296+
}
297+
}
298+
},
179299
)
180300
}
181301

app/src/main/java/io/github/xororz/localdream/ui/screens/ModelRunScreen.kt

Lines changed: 53 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ import io.github.xororz.localdream.ui.components.GenerationParamsDialog
176176
import io.github.xororz.localdream.ui.components.ImportParametersDialog
177177
import io.github.xororz.localdream.ui.components.OverlayIconButton
178178
import io.github.xororz.localdream.ui.components.PromptTagTextField
179+
import io.github.xororz.localdream.ui.components.ReproduceParametersDialog
179180
import io.github.xororz.localdream.ui.components.ShareParametersDialog
180181
import io.github.xororz.localdream.ui.components.ZoomableImageOverlay
181182
import io.github.xororz.localdream.utils.ImportedParams
@@ -443,7 +444,7 @@ fun ModelRunScreen(
443444
var showHistoryDetailDialog by remember { mutableStateOf(false) }
444445
var showHistoryParametersDialog by remember { mutableStateOf(false) }
445446
var showDeleteHistoryDialog by remember { mutableStateOf(false) }
446-
var showSeedConfirmDialog by remember { mutableStateOf(false) }
447+
var showReproduceParamsDialog by remember { mutableStateOf(false) }
447448
var pendingReproduceParams by remember { mutableStateOf<GenerationParameters?>(null) }
448449

449450
// Parameter share state
@@ -3139,7 +3140,7 @@ fun ModelRunScreen(
31393140
generationParams?.let {
31403141
pendingReproduceParams = it
31413142
showParametersDialog = false
3142-
showSeedConfirmDialog = true
3143+
showReproduceParamsDialog = true
31433144
}
31443145
},
31453146
onDismiss = { showParametersDialog = false },
@@ -3937,98 +3938,67 @@ fun ModelRunScreen(
39373938
onReproduce = {
39383939
pendingReproduceParams = selectedHistoryItem!!.params
39393940
showHistoryParametersDialog = false
3940-
showSeedConfirmDialog = true
3941+
showReproduceParamsDialog = true
39413942
},
39423943
onDismiss = { showHistoryParametersDialog = false },
39433944
)
39443945
}
39453946

3946-
// Seed confirmation dialog for reproduce
3947-
if (showSeedConfirmDialog && pendingReproduceParams != null) {
3947+
// Reproduce parameters dialog
3948+
if (showReproduceParamsDialog && pendingReproduceParams != null) {
39483949
val params = pendingReproduceParams!!
3949-
AlertDialog(
3950-
onDismissRequest = {
3951-
showSeedConfirmDialog = false
3950+
ReproduceParametersDialog(
3951+
params = params,
3952+
onApply = { selectedFields ->
3953+
if (ParamShareField.PROMPT in selectedFields) {
3954+
prompt = params.prompt
3955+
promptFieldValue = TextFieldValue(prompt, TextRange(prompt.length))
3956+
promptSuggestions = emptyList()
3957+
}
3958+
if (ParamShareField.NEGATIVE_PROMPT in selectedFields) {
3959+
negativePrompt = params.negativePrompt
3960+
negativePromptFieldValue =
3961+
TextFieldValue(negativePrompt, TextRange(negativePrompt.length))
3962+
negativePromptSuggestions = emptyList()
3963+
}
3964+
if (ParamShareField.STEPS in selectedFields) {
3965+
steps = params.steps.toFloat()
3966+
}
3967+
if (ParamShareField.CFG in selectedFields) {
3968+
cfg = params.cfg
3969+
}
3970+
if (ParamShareField.SEED in selectedFields) {
3971+
seed = params.seed?.toString() ?: ""
3972+
}
3973+
if (ParamShareField.SCHEDULER in selectedFields) {
3974+
scheduler = params.scheduler
3975+
}
3976+
if (ParamShareField.DENOISE_STRENGTH in selectedFields) {
3977+
denoiseStrength = params.denoiseStrength
3978+
}
3979+
if (model?.isSdxl == true) {
3980+
val newRatio = inferAspectRatioString(params.width, params.height)
3981+
if (newRatio != aspectRatio) {
3982+
aspectRatio = newRatio
3983+
clearImg2imgState()
3984+
}
3985+
}
3986+
saveAllFields()
3987+
3988+
showReproduceParamsDialog = false
39523989
pendingReproduceParams = null
39533990
showHistoryDetailDialog = false
39543991
selectedHistoryItem = null
3955-
},
3956-
title = { Text(stringResource(R.string.use_same_seed_title)) },
3957-
text = { Text(stringResource(R.string.use_same_seed_message, params.seed ?: "")) },
3958-
confirmButton = {
3959-
TextButton(
3960-
onClick = {
3961-
// Apply parameters with seed
3962-
prompt = params.prompt
3963-
negativePrompt = params.negativePrompt
3964-
promptFieldValue = TextFieldValue(prompt, TextRange(prompt.length))
3965-
negativePromptFieldValue =
3966-
TextFieldValue(negativePrompt, TextRange(negativePrompt.length))
3967-
promptSuggestions = emptyList()
3968-
negativePromptSuggestions = emptyList()
3969-
cfg = params.cfg
3970-
steps = params.steps.toFloat()
3971-
seed = params.seed?.toString() ?: ""
3972-
scheduler = params.scheduler
3973-
if (model?.isSdxl == true) {
3974-
val newRatio = inferAspectRatioString(params.width, params.height)
3975-
if (newRatio != aspectRatio) {
3976-
aspectRatio = newRatio
3977-
clearImg2imgState()
3978-
}
3979-
}
3980-
saveAllFields()
3981-
3982-
// Close dialogs and switch to prompt page
3983-
showSeedConfirmDialog = false
3984-
pendingReproduceParams = null
3985-
showHistoryDetailDialog = false
3986-
selectedHistoryItem = null
3987-
scope.launch {
3988-
pagerState.animateScrollToPage(0)
3989-
}
3990-
}
3991-
) {
3992-
Text(stringResource(R.string.yes))
3992+
scope.launch {
3993+
pagerState.animateScrollToPage(0)
39933994
}
39943995
},
3995-
dismissButton = {
3996-
TextButton(
3997-
onClick = {
3998-
// Apply parameters without seed
3999-
prompt = params.prompt
4000-
negativePrompt = params.negativePrompt
4001-
promptFieldValue = TextFieldValue(prompt, TextRange(prompt.length))
4002-
negativePromptFieldValue =
4003-
TextFieldValue(negativePrompt, TextRange(negativePrompt.length))
4004-
promptSuggestions = emptyList()
4005-
negativePromptSuggestions = emptyList()
4006-
cfg = params.cfg
4007-
steps = params.steps.toFloat()
4008-
seed = "" // Don't copy seed
4009-
scheduler = params.scheduler
4010-
if (model?.isSdxl == true) {
4011-
val newRatio = inferAspectRatioString(params.width, params.height)
4012-
if (newRatio != aspectRatio) {
4013-
aspectRatio = newRatio
4014-
clearImg2imgState()
4015-
}
4016-
}
4017-
saveAllFields()
4018-
4019-
// Close dialogs and switch to prompt page
4020-
showSeedConfirmDialog = false
4021-
pendingReproduceParams = null
4022-
showHistoryDetailDialog = false
4023-
selectedHistoryItem = null
4024-
scope.launch {
4025-
pagerState.animateScrollToPage(0)
4026-
}
4027-
}
4028-
) {
4029-
Text(stringResource(R.string.no))
4030-
}
4031-
}
3996+
onDismiss = {
3997+
showReproduceParamsDialog = false
3998+
pendingReproduceParams = null
3999+
showHistoryDetailDialog = false
4000+
selectedHistoryItem = null
4001+
},
40324002
)
40334003
}
40344004

app/src/main/res/values-ja/strings.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@
9999
<string name="no_history_hint">生成後に履歴が保存されます</string>
100100
<string name="selected_items_count">%d 件を選択中</string>
101101
<string name="generation_params_title">生成パラメータ</string>
102-
<string name="use_same_seed_title">同じシードを使用しますか?</string>
103-
<string name="use_same_seed_message">同じシード値(%s)を使用して画像を再現しますか?</string>
104102
<string name="delete_image">画像を削除</string>
105103
<string name="delete_image_confirm">この画像を削除してもよろしいですか? この操作は元に戻せません。</string>
106104
<string name="deleted">削除済み</string>
@@ -127,6 +125,10 @@
127125
<string name="use_last_seed">最後に利用したシードを利用 (%s)</string>
128126
<string name="batch_count">生成枚数: %d</string>
129127
<string name="reproduce">コピー</string>
128+
<string name="reproduce_params_title">パラメータを再現</string>
129+
<string name="reproduce_params_hint">プロンプトページに適用する項目を選択してください。</string>
130+
<string name="select_all">すべて選択</string>
131+
<string name="deselect_all">すべて解除</string>
130132
<string name="result_params">ステップ: %s / CFG: %.1f / シード: %s</string>
131133
<string name="result_params_2">サイズ: %s×%s / 処理時間: %s / %s</string>
132134
<string name="params_detail">パラメータの詳細</string>

app/src/main/res/values-zh/strings.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@
111111
<string name="no_history_hint">开始生成图片后会自动保存</string>
112112
<string name="selected_items_count">已选 %d 项</string>
113113
<string name="generation_params_title">生成参数</string>
114-
<string name="use_same_seed_title">使用相同的种子吗?</string>
115-
<string name="use_same_seed_message">是否使用相同的种子值 (%s) 来重现图片?\n\n选择\"是\"将使用相同种子,选择\"否\"将仅复用参数。</string>
116114
<string name="delete_image">删除图片</string>
117115
<string name="delete_image_confirm">确定要删除这张图片吗?此操作无法撤销。</string>
118116
<string name="deleted">已删除</string>
@@ -141,6 +139,10 @@
141139
<string name="use_last_seed">使用上次的种子 (%s)</string>
142140
<string name="batch_count">批次数量: %d</string>
143141
<string name="reproduce">复制</string>
142+
<string name="reproduce_params_title">复用参数</string>
143+
<string name="reproduce_params_hint">选择要应用到提示词页面的字段。</string>
144+
<string name="select_all">全选</string>
145+
<string name="deselect_all">全不选</string>
144146
<string name="result_params">步数: %s / CFG: %.1f / 种子: %s</string>
145147
<string name="result_params_2">尺寸: %sx%s / 耗时: %s / %s</string>
146148
<string name="params_detail">生成参数详情</string>

app/src/main/res/values/strings.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@
111111
<string name="no_history_hint">History will be saved after generation</string>
112112
<string name="selected_items_count">Selected %d items</string>
113113
<string name="generation_params_title">Generation Parameters</string>
114-
<string name="use_same_seed_title">Use same seed?</string>
115-
<string name="use_same_seed_message">Use the same seed value (%s) to reproduce the image?\n\nSelect \"Yes\" to use the same seed, select \"No\" to only reuse parameters.</string>
116114
<string name="delete_image">Delete Image</string>
117115
<string name="delete_image_confirm">Are you sure you want to delete this image? This action cannot be undone.</string>
118116
<string name="deleted">Deleted</string>
@@ -141,6 +139,10 @@
141139
<string name="use_last_seed">Use Last Seed (%s)</string>
142140
<string name="batch_count">Batch Count: %d</string>
143141
<string name="reproduce">Reproduce</string>
142+
<string name="reproduce_params_title">Reproduce Parameters</string>
143+
<string name="reproduce_params_hint">Pick the fields to apply to the prompt page.</string>
144+
<string name="select_all">Select all</string>
145+
<string name="deselect_all">Deselect all</string>
144146
<string name="result_params">steps: %s / CFG: %.1f / seed: %s</string>
145147
<string name="result_params_2">size: %sx%s / time: %s / %s</string>
146148
<string name="params_detail">Details</string>

0 commit comments

Comments
 (0)