Skip to content

Commit 208654b

Browse files
authored
dialog: add maxWidth support for OverlayDialog and WindowDialog (#370)
- Add `maxWidth` parameter to `OverlayDialog`, `WindowDialog`, and `DialogContentLayout`. - Define `DialogDefaults.MaxWidth` and document the new property in EN/ZH. - Add responsive "Wide Dialog" examples showing portrait vs. landscape behavior. - Rename internal state variables in `DialogSection` for better clarity.
1 parent 2027fe1 commit 208654b

8 files changed

Lines changed: 214 additions & 11 deletions

File tree

docs/components/overlaydialog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Scaffold {
7575
| insideMargin | DpSize | Dialog internal content margin | DialogDefaults.insideMargin | No |
7676
| defaultWindowInsetsPadding | Boolean | Whether to apply default window insets padding | true | No |
7777
| renderInRootScaffold | Boolean | Whether to render the dialog in the root (outermost) Scaffold. When true, the dialog covers the full screen. When false, it renders within the current Scaffold's bounds | true | No |
78+
| maxWidth | Dp | Maximum width of the dialog | DialogDefaults.MaxWidth | No |
7879
| content | @Composable () -> Unit | Dialog content | - | Yes |
7980

8081
### DialogDefaults Object
@@ -85,6 +86,7 @@ The DialogDefaults object provides default settings for the OverlayDialog compon
8586

8687
| Property Name | Type | Description |
8788
| ------------- | ------ | ------------------------------ |
89+
| MaxWidth | Dp | Default maximum dialog width |
8890
| outsideMargin | DpSize | Default dialog external margin |
8991
| insideMargin | DpSize | Default dialog internal margin |
9092

docs/components/windowdialog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ WindowDialog(
7171
| outsideMargin | DpSize | Outer margin (window edges) | DialogDefaults.outsideMargin | No |
7272
| insideMargin | DpSize | Inner padding for dialog content | DialogDefaults.insideMargin | No |
7373
| defaultWindowInsetsPadding | Boolean | Apply default insets padding (IME, nav, caption) | true | No |
74+
| maxWidth | Dp | Maximum width of the dialog | DialogDefaults.MaxWidth | No |
7475
| content | @Composable () -> Unit | Dialog content | - | Yes |
7576

7677
### DialogDefaults
@@ -79,6 +80,7 @@ WindowDialog(
7980

8081
| Name | Type | Description |
8182
| ------------- | ------ | -------------------------------- |
83+
| MaxWidth | Dp | Default maximum dialog width |
8284
| outsideMargin | DpSize | Default outer margin for dialog |
8385
| insideMargin | DpSize | Default inner padding for dialog |
8486

docs/zh_CN/components/overlaydialog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Scaffold {
7575
| insideMargin | DpSize | 对话框内部内容的边距 | DialogDefaults.insideMargin ||
7676
| defaultWindowInsetsPadding | Boolean | 是否应用默认窗口插入内边距 | true ||
7777
| renderInRootScaffold | Boolean | 是否在根(最外层)Scaffold 中渲染对话框。为 true 时,对话框覆盖全屏。为 false 时,在当前 Scaffold 的范围内渲染 | true ||
78+
| maxWidth | Dp | 对话框最大宽度 | DialogDefaults.MaxWidth ||
7879
| content | @Composable () -> Unit | 对话框的内容 | - ||
7980

8081
### DialogDefaults
@@ -83,6 +84,7 @@ Scaffold {
8384

8485
| 属性名 | 类型 | 说明 |
8586
| ------------- | ------ | ------------------ |
87+
| MaxWidth | Dp | 对话框默认最大宽度 |
8688
| outsideMargin | DpSize | 对话框外部默认边距 |
8789
| insideMargin | DpSize | 对话框内部默认边距 |
8890

docs/zh_CN/components/windowdialog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ WindowDialog(
7171
| outsideMargin | DpSize | 相对窗口边缘的外部边距 | DialogDefaults.outsideMargin ||
7272
| insideMargin | DpSize | 对话框内容内部边距 | DialogDefaults.insideMargin ||
7373
| defaultWindowInsetsPadding | Boolean | 是否应用默认窗口插入内边距(输入法/导航/标题) | true ||
74+
| maxWidth | Dp | 对话框最大宽度 | DialogDefaults.MaxWidth ||
7475
| content | @Composable () -> Unit | 对话框内容 | - ||
7576

7677
### DialogDefaults
@@ -79,6 +80,7 @@ WindowDialog(
7980

8081
| 属性名 | 类型 | 说明 |
8182
| ------------- | ------ | ------------------ |
83+
| MaxWidth | Dp | 对话框默认最大宽度 |
8284
| outsideMargin | DpSize | 对话框外部默认边距 |
8385
| insideMargin | DpSize | 对话框内部默认边距 |
8486

example/shared/src/commonMain/kotlin/component/DialogSection.kt

Lines changed: 193 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44
package component
55

66
import androidx.compose.foundation.layout.Arrangement
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.IntrinsicSize
79
import androidx.compose.foundation.layout.Row
810
import androidx.compose.foundation.layout.Spacer
11+
import androidx.compose.foundation.layout.fillMaxHeight
12+
import androidx.compose.foundation.layout.fillMaxWidth
13+
import androidx.compose.foundation.layout.height
914
import androidx.compose.foundation.layout.padding
1015
import androidx.compose.foundation.layout.width
1116
import androidx.compose.foundation.lazy.LazyListScope
@@ -14,23 +19,35 @@ import androidx.compose.runtime.getValue
1419
import androidx.compose.runtime.mutableStateOf
1520
import androidx.compose.runtime.remember
1621
import androidx.compose.runtime.setValue
22+
import androidx.compose.ui.Alignment
1723
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.platform.LocalWindowInfo
25+
import androidx.compose.ui.text.font.FontWeight
26+
import androidx.compose.ui.text.style.TextAlign
1827
import androidx.compose.ui.unit.dp
1928
import top.yukonga.miuix.kmp.basic.ButtonDefaults
2029
import top.yukonga.miuix.kmp.basic.Card
2130
import top.yukonga.miuix.kmp.basic.SmallTitle
31+
import top.yukonga.miuix.kmp.basic.Text
2232
import top.yukonga.miuix.kmp.basic.TextButton
33+
import top.yukonga.miuix.kmp.basic.VerticalDivider
34+
import top.yukonga.miuix.kmp.layout.DialogDefaults
2335
import top.yukonga.miuix.kmp.overlay.OverlayDialog
2436
import top.yukonga.miuix.kmp.preference.ArrowPreference
2537
import top.yukonga.miuix.kmp.theme.LocalDismissState
38+
import top.yukonga.miuix.kmp.theme.MiuixTheme
2639
import top.yukonga.miuix.kmp.window.WindowDialog
2740

2841
fun LazyListScope.dialogSection() {
2942
item(key = "dialog") {
30-
var showSuperDialog by remember { mutableStateOf(false) }
43+
var showOverlayDialog by remember { mutableStateOf(false) }
3144
var showWindowDialog by remember { mutableStateOf(false) }
32-
var superDialogHoldDown by remember { mutableStateOf(false) }
45+
var overlayDialogHoldDown by remember { mutableStateOf(false) }
3346
var windowDialogHoldDown by remember { mutableStateOf(false) }
47+
var showWideSuperDialog by remember { mutableStateOf(false) }
48+
var showWideWindowDialog by remember { mutableStateOf(false) }
49+
var wideSuperDialogHoldDown by remember { mutableStateOf(false) }
50+
var wideWindowDialogHoldDown by remember { mutableStateOf(false) }
3451

3552
SmallTitle(text = "Dialog")
3653
Card(
@@ -42,10 +59,10 @@ fun LazyListScope.dialogSection() {
4259
title = "Dialog (O)",
4360
summary = "Click to show an OverlayDialog",
4461
onClick = {
45-
showSuperDialog = true
46-
superDialogHoldDown = true
62+
showOverlayDialog = true
63+
overlayDialogHoldDown = true
4764
},
48-
holdDownState = superDialogHoldDown,
65+
holdDownState = overlayDialogHoldDown,
4966
)
5067
ArrowPreference(
5168
title = "Dialog (W)",
@@ -56,23 +73,51 @@ fun LazyListScope.dialogSection() {
5673
},
5774
holdDownState = windowDialogHoldDown,
5875
)
76+
ArrowPreference(
77+
title = "Wide Dialog (O)",
78+
summary = "Portrait shows a regular dialog; landscape shows a two-column dialog",
79+
onClick = {
80+
showWideSuperDialog = true
81+
wideSuperDialogHoldDown = true
82+
},
83+
holdDownState = wideSuperDialogHoldDown,
84+
)
85+
ArrowPreference(
86+
title = "Wide Dialog (W)",
87+
summary = "Portrait shows a regular dialog; landscape shows a two-column dialog",
88+
onClick = {
89+
showWideWindowDialog = true
90+
wideWindowDialogHoldDown = true
91+
},
92+
holdDownState = wideWindowDialogHoldDown,
93+
)
5994
}
6095

61-
SuperDialogDemo(
62-
show = showSuperDialog,
63-
onDismissRequest = { showSuperDialog = false },
64-
onDismissFinished = { superDialogHoldDown = false },
96+
OverlayDialogDemo(
97+
show = showOverlayDialog,
98+
onDismissRequest = { showOverlayDialog = false },
99+
onDismissFinished = { overlayDialogHoldDown = false },
65100
)
66101
WindowDialogDemo(
67102
show = showWindowDialog,
68103
onDismissRequest = { showWindowDialog = false },
69104
onDismissFinished = { windowDialogHoldDown = false },
70105
)
106+
WideSuperDialogDemo(
107+
show = showWideSuperDialog,
108+
onDismissRequest = { showWideSuperDialog = false },
109+
onDismissFinished = { wideSuperDialogHoldDown = false },
110+
)
111+
WideWindowDialogDemo(
112+
show = showWideWindowDialog,
113+
onDismissRequest = { showWideWindowDialog = false },
114+
onDismissFinished = { wideWindowDialogHoldDown = false },
115+
)
71116
}
72117
}
73118

74119
@Composable
75-
private fun SuperDialogDemo(
120+
private fun OverlayDialogDemo(
76121
show: Boolean,
77122
onDismissRequest: () -> Unit,
78123
onDismissFinished: () -> Unit,
@@ -137,3 +182,141 @@ private fun WindowDialogDemo(
137182
},
138183
)
139184
}
185+
186+
@Composable
187+
private fun WideSuperDialogDemo(
188+
show: Boolean,
189+
onDismissRequest: () -> Unit,
190+
onDismissFinished: () -> Unit,
191+
) {
192+
val windowSize = LocalWindowInfo.current.containerDpSize
193+
val isLandscape = windowSize.width > windowSize.height
194+
195+
OverlayDialog(
196+
show = show,
197+
title = if (isLandscape) null else "Wide Dialog",
198+
summary = if (isLandscape) null else "Rotate to landscape to see the effect.",
199+
maxWidth = if (isLandscape) 560.dp else DialogDefaults.MaxWidth,
200+
onDismissRequest = onDismissRequest,
201+
onDismissFinished = onDismissFinished,
202+
content = {
203+
WideDialogContent(isLandscape = isLandscape)
204+
},
205+
)
206+
}
207+
208+
@Composable
209+
private fun WideWindowDialogDemo(
210+
show: Boolean,
211+
onDismissRequest: () -> Unit,
212+
onDismissFinished: () -> Unit,
213+
) {
214+
val windowSize = LocalWindowInfo.current.containerDpSize
215+
val isLandscape = windowSize.width > windowSize.height
216+
217+
WindowDialog(
218+
show = show,
219+
title = if (isLandscape) null else "Wide Dialog",
220+
summary = if (isLandscape) null else "Rotate to landscape to see the effect.",
221+
maxWidth = if (isLandscape) 560.dp else DialogDefaults.MaxWidth,
222+
onDismissRequest = onDismissRequest,
223+
onDismissFinished = onDismissFinished,
224+
content = {
225+
WideDialogContent(isLandscape = isLandscape)
226+
},
227+
)
228+
}
229+
230+
@Composable
231+
private fun WideDialogContent(
232+
isLandscape: Boolean,
233+
) {
234+
val dismissState = LocalDismissState.current
235+
236+
if (isLandscape) {
237+
Row(
238+
modifier = Modifier
239+
.fillMaxWidth()
240+
.height(IntrinsicSize.Min),
241+
) {
242+
Column(
243+
modifier = Modifier
244+
.weight(1f)
245+
.fillMaxHeight(),
246+
verticalArrangement = Arrangement.Center,
247+
horizontalAlignment = Alignment.CenterHorizontally,
248+
) {
249+
Text(
250+
modifier = Modifier.fillMaxWidth(),
251+
text = "Wide Dialog",
252+
style = MiuixTheme.textStyles.title4,
253+
color = MiuixTheme.colorScheme.onBackground,
254+
fontWeight = FontWeight.Medium,
255+
textAlign = TextAlign.Center,
256+
)
257+
Spacer(Modifier.height(8.dp))
258+
Text(
259+
modifier = Modifier.fillMaxWidth(),
260+
text = "Rotate to landscape to see the effect.",
261+
style = MiuixTheme.textStyles.body1,
262+
color = MiuixTheme.colorScheme.onSurfaceSecondary,
263+
textAlign = TextAlign.Center,
264+
)
265+
}
266+
267+
VerticalDivider(
268+
modifier = Modifier
269+
.fillMaxHeight()
270+
.padding(horizontal = 20.dp),
271+
)
272+
273+
Column(
274+
modifier = Modifier
275+
.weight(1f)
276+
.fillMaxHeight(),
277+
verticalArrangement = Arrangement.spacedBy(
278+
space = 12.dp,
279+
alignment = Alignment.CenterVertically,
280+
),
281+
) {
282+
TextButton(
283+
text = "Allow Once",
284+
onClick = { dismissState?.invoke() },
285+
modifier = Modifier.fillMaxWidth(),
286+
colors = ButtonDefaults.textButtonColorsPrimary(),
287+
)
288+
TextButton(
289+
text = "Always Allow",
290+
onClick = { dismissState?.invoke() },
291+
modifier = Modifier.fillMaxWidth(),
292+
)
293+
TextButton(
294+
text = "Deny",
295+
onClick = { dismissState?.invoke() },
296+
modifier = Modifier.fillMaxWidth(),
297+
)
298+
}
299+
}
300+
} else {
301+
Column(
302+
verticalArrangement = Arrangement.spacedBy(12.dp),
303+
) {
304+
TextButton(
305+
text = "Allow Once",
306+
onClick = { dismissState?.invoke() },
307+
modifier = Modifier.fillMaxWidth(),
308+
colors = ButtonDefaults.textButtonColorsPrimary(),
309+
)
310+
TextButton(
311+
text = "Always Allow",
312+
onClick = { dismissState?.invoke() },
313+
modifier = Modifier.fillMaxWidth(),
314+
)
315+
TextButton(
316+
text = "Deny",
317+
onClick = { dismissState?.invoke() },
318+
modifier = Modifier.fillMaxWidth(),
319+
)
320+
}
321+
}
322+
}

miuix-ui/src/commonMain/kotlin/top/yukonga/miuix/kmp/layout/DialogContentLayout.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ import top.yukonga.miuix.kmp.window.WindowDialog
8989
* is cancelled mid-flight (e.g., by [show] toggling back to true).
9090
* @param defaultWindowInsetsPadding Whether to apply default window insets padding.
9191
* @param topInset Optional top inset override. If null, calculated from window insets.
92+
* @param maxWidth The maximum width of the dialog.
9293
* @param content The content of the dialog.
9394
*/
9495
@Suppress("ktlint:compose:modifier-not-used-at-root")
@@ -109,6 +110,7 @@ internal fun DialogContentLayout(
109110
onDismissFinished: (() -> Unit)? = null,
110111
defaultWindowInsetsPadding: Boolean = true,
111112
topInset: Dp? = null,
113+
maxWidth: Dp = DialogDefaults.MaxWidth,
112114
content: @Composable () -> Unit,
113115
) {
114116
val animationProgress = remember { Animatable(0f, visibilityThreshold = 0.0001f) }
@@ -244,6 +246,7 @@ internal fun DialogContentLayout(
244246
onDismissRequest = requestDismiss,
245247
modifier = contentModifier,
246248
topInset = topInset,
249+
maxWidth = maxWidth,
247250
content = {
248251
CompositionLocalProvider(LocalDismissState provides requestDismiss) {
249252
content()
@@ -269,6 +272,7 @@ internal fun DialogContent(
269272
onDismissRequest: (() -> Unit)?,
270273
modifier: Modifier = Modifier,
271274
topInset: Dp? = null,
275+
maxWidth: Dp = DialogDefaults.MaxWidth,
272276
content: @Composable () -> Unit,
273277
) {
274278
val density = LocalDensity.current
@@ -307,7 +311,7 @@ internal fun DialogContent(
307311
}
308312

309313
val contentModifier = modifier
310-
.widthIn(max = DialogDefaults.MaxWidth)
314+
.widthIn(max = maxWidth)
311315
.heightIn(max = if (isLargeScreen) windowHeight * (2f / 3f) else Dp.Unspecified)
312316
.onGloballyPositioned { coordinates ->
313317
dialogHeightPx.intValue = coordinates.size.height

0 commit comments

Comments
 (0)