0
# Window and Platform Management
1
2
Platform-specific window management and integration capabilities for Desktop, iOS, Android, and Web platforms, providing native-like application experiences across all supported targets.
3
4
## Capabilities
5
6
### Desktop Window Management
7
8
Comprehensive window management system for desktop applications with support for multiple windows, window states, and native platform integration.
9
10
```kotlin { .api }
11
/**
12
* Creates a single window application.
13
*/
14
@Composable
15
fun singleWindowApplication(
16
state: WindowState = rememberWindowState(),
17
visible: Boolean = true,
18
title: String = "Untitled",
19
icon: Painter? = null,
20
undecorated: Boolean = false,
21
transparent: Boolean = false,
22
resizable: Boolean = true,
23
enabled: Boolean = true,
24
focusable: Boolean = true,
25
alwaysOnTop: Boolean = false,
26
onPreviewKeyEvent: (KeyEvent) -> Boolean = { false },
27
onKeyEvent: (KeyEvent) -> Boolean = { false },
28
content: @Composable WindowScope.() -> Unit
29
)
30
31
/**
32
* Creates a window application with multiple windows.
33
*/
34
@Composable
35
fun application(
36
exitProcessOnExit: Boolean = true,
37
content: @Composable ApplicationScope.() -> Unit
38
)
39
40
/**
41
* Creates a window within an application.
42
*/
43
@Composable
44
fun ApplicationScope.Window(
45
onCloseRequest: () -> Unit,
46
state: WindowState = rememberWindowState(),
47
visible: Boolean = true,
48
title: String = "Untitled",
49
icon: Painter? = null,
50
undecorated: Boolean = false,
51
transparent: Boolean = false,
52
resizable: Boolean = true,
53
enabled: Boolean = true,
54
focusable: Boolean = true,
55
alwaysOnTop: Boolean = false,
56
onPreviewKeyEvent: (KeyEvent) -> Boolean = { false },
57
onKeyEvent: (KeyEvent) -> Boolean = { false },
58
content: @Composable WindowScope.() -> Unit
59
)
60
61
/**
62
* State holder for window properties.
63
*/
64
@Stable
65
class WindowState(
66
placement: WindowPlacement = WindowPlacement.Floating,
67
isMinimized: Boolean = false,
68
position: WindowPosition = WindowPosition.PlatformDefault,
69
size: DpSize = DpSize.Unspecified
70
) {
71
/**
72
* Current window placement mode.
73
*/
74
var placement: WindowPlacement by mutableStateOf(placement)
75
76
/**
77
* Whether the window is minimized.
78
*/
79
var isMinimized: Boolean by mutableStateOf(isMinimized)
80
81
/**
82
* Current window position.
83
*/
84
var position: WindowPosition by mutableStateOf(position)
85
86
/**
87
* Current window size.
88
*/
89
var size: DpSize by mutableStateOf(size)
90
}
91
92
/**
93
* Remember a WindowState across recompositions.
94
*/
95
@Composable
96
fun rememberWindowState(
97
placement: WindowPlacement = WindowPlacement.Floating,
98
isMinimized: Boolean = false,
99
position: WindowPosition = WindowPosition.PlatformDefault,
100
size: DpSize = DpSize.Unspecified
101
): WindowState
102
103
/**
104
* Window placement modes.
105
*/
106
enum class WindowPlacement {
107
Floating, Maximized, Fullscreen
108
}
109
110
/**
111
* Window position specification.
112
*/
113
sealed class WindowPosition {
114
object PlatformDefault : WindowPosition()
115
data class Absolute(val x: Dp, val y: Dp) : WindowPosition()
116
data class Aligned(val alignment: Alignment) : WindowPosition()
117
}
118
119
/**
120
* Size specification for windows.
121
*/
122
@Immutable
123
data class DpSize(
124
val width: Dp,
125
val height: Dp
126
) {
127
companion object {
128
val Unspecified: DpSize = DpSize(Dp.Unspecified, Dp.Unspecified)
129
val Zero: DpSize = DpSize(0.dp, 0.dp)
130
}
131
}
132
133
/**
134
* Scope for window content.
135
*/
136
interface WindowScope {
137
/**
138
* The current window state.
139
*/
140
val window: ComposeWindow
141
}
142
143
/**
144
* Scope for application content.
145
*/
146
interface ApplicationScope {
147
/**
148
* Exit the application.
149
*/
150
fun exitApplication()
151
}
152
```
153
154
**Usage Examples:**
155
156
```kotlin
157
// Single window application
158
fun main() = singleWindowApplication(
159
title = "My Desktop App",
160
state = rememberWindowState(
161
placement = WindowPlacement.Floating,
162
size = DpSize(800.dp, 600.dp),
163
position = WindowPosition.Aligned(Alignment.Center)
164
),
165
resizable = true,
166
icon = painterResource("app_icon.png")
167
) {
168
Column(
169
modifier = Modifier.fillMaxSize().padding(16.dp)
170
) {
171
Text("Desktop Application")
172
Button(onClick = {
173
window.close()
174
}) {
175
Text("Close Window")
176
}
177
}
178
}
179
180
// Multi-window application
181
fun main() = application {
182
var isSecondWindowVisible by remember { mutableStateOf(false) }
183
184
Window(
185
onCloseRequest = ::exitApplication,
186
title = "Main Window",
187
state = rememberWindowState(size = DpSize(600.dp, 400.dp))
188
) {
189
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
190
Text("Main Window")
191
Button(onClick = { isSecondWindowVisible = true }) {
192
Text("Open Second Window")
193
}
194
}
195
}
196
197
if (isSecondWindowVisible) {
198
Window(
199
onCloseRequest = { isSecondWindowVisible = false },
200
title = "Second Window",
201
state = rememberWindowState(
202
position = WindowPosition.Absolute(100.dp, 100.dp),
203
size = DpSize(400.dp, 300.dp)
204
)
205
) {
206
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
207
Text("Second Window")
208
Button(onClick = { isSecondWindowVisible = false }) {
209
Text("Close")
210
}
211
}
212
}
213
}
214
}
215
216
// Window with custom properties
217
fun main() = singleWindowApplication(
218
title = "Custom Window",
219
undecorated = true,
220
transparent = true,
221
alwaysOnTop = true,
222
state = rememberWindowState(
223
placement = WindowPlacement.Floating,
224
size = DpSize(300.dp, 200.dp)
225
)
226
) {
227
Box(
228
modifier = Modifier
229
.fillMaxSize()
230
.background(
231
Color.Black.copy(alpha = 0.8f),
232
RoundedCornerShape(8.dp)
233
)
234
) {
235
Text(
236
text = "Custom Window",
237
color = Color.White,
238
modifier = Modifier.align(Alignment.Center)
239
)
240
}
241
}
242
```
243
244
### iOS Platform Integration
245
246
Integration layer for iOS applications using UIKit interoperability and native iOS features.
247
248
```kotlin { .api }
249
/**
250
* Creates a UIViewController that hosts Compose content.
251
*/
252
fun ComposeUIViewController(
253
configure: ComposeUIViewControllerConfiguration = ComposeUIViewControllerConfiguration(),
254
content: @Composable () -> Unit
255
): UIViewController
256
257
/**
258
* Configuration for Compose UIViewController.
259
*/
260
class ComposeUIViewControllerConfiguration {
261
/**
262
* Whether the view controller should automatically adjust for safe areas.
263
*/
264
var automaticallyAdjustForSafeArea: Boolean = true
265
266
/**
267
* Background color for the view controller.
268
*/
269
var backgroundColor: UIColor? = null
270
271
/**
272
* Whether to use system gestures.
273
*/
274
var systemGestures: Boolean = true
275
276
/**
277
* Status bar style preference.
278
*/
279
var statusBarStyle: UIStatusBarStyle? = null
280
}
281
282
/**
283
* Access to iOS-specific platform features.
284
*/
285
object IOSPlatform {
286
/**
287
* Get the current safe area insets.
288
*/
289
val safeAreaInsets: EdgeInsets
290
291
/**
292
* Whether the device is in dark mode.
293
*/
294
val isDarkMode: Boolean
295
296
/**
297
* Current device orientation.
298
*/
299
val deviceOrientation: DeviceOrientation
300
301
/**
302
* Show/hide the status bar.
303
*/
304
fun setStatusBarHidden(hidden: Boolean, animated: Boolean = true)
305
306
/**
307
* Present a native iOS alert.
308
*/
309
suspend fun showAlert(
310
title: String,
311
message: String,
312
primaryButton: String = "OK",
313
secondaryButton: String? = null
314
): Boolean
315
}
316
317
/**
318
* Device orientation states.
319
*/
320
enum class DeviceOrientation {
321
Portrait, PortraitUpsideDown, LandscapeLeft, LandscapeRight, Unknown
322
}
323
324
/**
325
* Edge insets for safe areas.
326
*/
327
@Immutable
328
data class EdgeInsets(
329
val top: Dp,
330
val leading: Dp,
331
val bottom: Dp,
332
val trailing: Dp
333
)
334
335
/**
336
* CompositionLocal for accessing iOS platform features.
337
*/
338
val LocalIOSPlatform: ProvidableCompositionLocal<IOSPlatform>
339
```
340
341
**Usage Examples:**
342
343
```kotlin
344
// Basic iOS integration
345
fun createMainViewController() = ComposeUIViewController(
346
configure = ComposeUIViewControllerConfiguration().apply {
347
automaticallyAdjustForSafeArea = true
348
backgroundColor = UIColor.systemBackgroundColor
349
statusBarStyle = UIStatusBarStyle.UIStatusBarStyleDefault
350
}
351
) {
352
IOSApp()
353
}
354
355
@Composable
356
fun IOSApp() {
357
val platform = LocalIOSPlatform.current
358
val safeAreaInsets = platform.safeAreaInsets
359
360
Column(
361
modifier = Modifier
362
.fillMaxSize()
363
.padding(
364
top = safeAreaInsets.top,
365
bottom = safeAreaInsets.bottom,
366
start = safeAreaInsets.leading,
367
end = safeAreaInsets.trailing
368
)
369
) {
370
Text("iOS Application")
371
372
Button(onClick = {
373
// Show native iOS alert
374
coroutineScope.launch {
375
val result = platform.showAlert(
376
title = "Confirmation",
377
message = "Are you sure?",
378
primaryButton = "Yes",
379
secondaryButton = "No"
380
)
381
if (result) {
382
// User tapped "Yes"
383
}
384
}
385
}) {
386
Text("Show Alert")
387
}
388
389
Text("Dark mode: ${platform.isDarkMode}")
390
Text("Orientation: ${platform.deviceOrientation}")
391
}
392
}
393
394
// Custom iOS view controller integration
395
class CustomIOSViewController : UIViewController {
396
private val composeView = ComposeUIViewController {
397
MyComposeContent()
398
}
399
400
override fun viewDidLoad() {
401
super.viewDidLoad()
402
403
// Add Compose content as child view controller
404
addChild(composeView)
405
view.addSubview(composeView.view)
406
composeView.didMove(toParent = this)
407
408
// Setup constraints
409
composeView.view.translatesAutoresizingMaskIntoConstraints = false
410
NSLayoutConstraint.activate(listOf(
411
composeView.view.topAnchor.constraint(equalTo = view.safeAreaLayoutGuide.topAnchor),
412
composeView.view.leadingAnchor.constraint(equalTo = view.leadingAnchor),
413
composeView.view.trailingAnchor.constraint(equalTo = view.trailingAnchor),
414
composeView.view.bottomAnchor.constraint(equalTo = view.safeAreaLayoutGuide.bottomAnchor)
415
))
416
}
417
}
418
```
419
420
### Web Platform Integration
421
422
Web-specific window and platform features for Compose Multiplatform web applications.
423
424
```kotlin { .api }
425
/**
426
* Creates a Compose application for the web platform.
427
*/
428
fun CanvasBasedWindow(
429
canvasElementId: String? = null,
430
title: String = "Compose Application",
431
content: @Composable () -> Unit
432
)
433
434
/**
435
* Access to web-specific platform features.
436
*/
437
object WebPlatform {
438
/**
439
* The current browser window.
440
*/
441
val window: Window
442
443
/**
444
* The current document.
445
*/
446
val document: Document
447
448
/**
449
* Current viewport size.
450
*/
451
val viewportSize: IntSize
452
453
/**
454
* Whether the page is loaded via HTTPS.
455
*/
456
val isSecure: Boolean
457
458
/**
459
* Current page URL.
460
*/
461
val currentUrl: String
462
463
/**
464
* Navigate to a new URL.
465
*/
466
fun navigateTo(url: String)
467
468
/**
469
* Show a browser alert dialog.
470
*/
471
fun alert(message: String)
472
473
/**
474
* Show a browser confirmation dialog.
475
*/
476
fun confirm(message: String): Boolean
477
478
/**
479
* Download a file.
480
*/
481
fun downloadFile(data: ByteArray, fileName: String, mimeType: String = "application/octet-stream")
482
483
/**
484
* Copy text to clipboard.
485
*/
486
suspend fun copyToClipboard(text: String): Boolean
487
488
/**
489
* Read text from clipboard.
490
*/
491
suspend fun readFromClipboard(): String?
492
}
493
494
/**
495
* Browser storage access.
496
*/
497
object WebStorage {
498
/**
499
* Local storage operations.
500
*/
501
object Local {
502
fun setItem(key: String, value: String)
503
fun getItem(key: String): String?
504
fun removeItem(key: String)
505
fun clear()
506
val length: Int
507
}
508
509
/**
510
* Session storage operations.
511
*/
512
object Session {
513
fun setItem(key: String, value: String)
514
fun getItem(key: String): String?
515
fun removeItem(key: String)
516
fun clear()
517
val length: Int
518
}
519
}
520
521
/**
522
* CompositionLocal for accessing web platform features.
523
*/
524
val LocalWebPlatform: ProvidableCompositionLocal<WebPlatform>
525
```
526
527
**Usage Examples:**
528
529
```kotlin
530
// Web application setup
531
fun main() {
532
CanvasBasedWindow(
533
canvasElementId = "ComposeTarget",
534
title = "My Web App"
535
) {
536
WebApp()
537
}
538
}
539
540
@Composable
541
fun WebApp() {
542
val platform = LocalWebPlatform.current
543
var viewportSize by remember { mutableStateOf(platform.viewportSize) }
544
var clipboardContent by remember { mutableStateOf("") }
545
546
// Update viewport size on window resize
547
LaunchedEffect(Unit) {
548
// Listen for window resize events
549
platform.window.addEventListener("resize") {
550
viewportSize = platform.viewportSize
551
}
552
}
553
554
Column(
555
modifier = Modifier
556
.fillMaxSize()
557
.padding(16.dp)
558
) {
559
Text("Web Application")
560
Text("Viewport: ${viewportSize.width} x ${viewportSize.height}")
561
Text("URL: ${platform.currentUrl}")
562
Text("Secure: ${platform.isSecure}")
563
564
Spacer(modifier = Modifier.height(16.dp))
565
566
Row {
567
Button(onClick = {
568
platform.alert("Hello from Compose!")
569
}) {
570
Text("Show Alert")
571
}
572
573
Spacer(modifier = Modifier.width(8.dp))
574
575
Button(onClick = {
576
val confirmed = platform.confirm("Are you sure?")
577
if (confirmed) {
578
platform.alert("Confirmed!")
579
}
580
}) {
581
Text("Show Confirm")
582
}
583
}
584
585
Spacer(modifier = Modifier.height(16.dp))
586
587
// Clipboard operations
588
TextField(
589
value = clipboardContent,
590
onValueChange = { clipboardContent = it },
591
label = { Text("Clipboard content") },
592
modifier = Modifier.fillMaxWidth()
593
)
594
595
Row {
596
Button(onClick = {
597
coroutineScope.launch {
598
platform.copyToClipboard(clipboardContent)
599
}
600
}) {
601
Text("Copy to Clipboard")
602
}
603
604
Spacer(modifier = Modifier.width(8.dp))
605
606
Button(onClick = {
607
coroutineScope.launch {
608
val content = platform.readFromClipboard()
609
if (content != null) {
610
clipboardContent = content
611
}
612
}
613
}) {
614
Text("Read from Clipboard")
615
}
616
}
617
618
Spacer(modifier = Modifier.height(16.dp))
619
620
// File download
621
Button(onClick = {
622
val data = "Hello, World!".toByteArray()
623
platform.downloadFile(data, "hello.txt", "text/plain")
624
}) {
625
Text("Download File")
626
}
627
628
// Local storage operations
629
Button(onClick = {
630
WebStorage.Local.setItem("myKey", clipboardContent)
631
platform.alert("Saved to local storage!")
632
}) {
633
Text("Save to Local Storage")
634
}
635
636
Button(onClick = {
637
val stored = WebStorage.Local.getItem("myKey")
638
if (stored != null) {
639
clipboardContent = stored
640
}
641
}) {
642
Text("Load from Local Storage")
643
}
644
}
645
}
646
```
647
648
### Dialog and Modal Management
649
650
Cross-platform dialog and modal window management for user interactions and confirmations.
651
652
```kotlin { .api }
653
/**
654
* A modal dialog window.
655
*/
656
@Composable
657
fun Dialog(
658
onDismissRequest: () -> Unit,
659
properties: DialogProperties = DialogProperties(),
660
content: @Composable () -> Unit
661
)
662
663
/**
664
* Properties for configuring dialog behavior.
665
*/
666
@Immutable
667
data class DialogProperties(
668
val dismissOnBackPress: Boolean = true,
669
val dismissOnClickOutside: Boolean = true,
670
val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
671
val usePlatformDefaultWidth: Boolean = true,
672
val decorFitsSystemWindows: Boolean = true
673
)
674
675
/**
676
* Security policies for dialog windows.
677
*/
678
enum class SecureFlagPolicy {
679
Inherit, SecureOn, SecureOff
680
}
681
682
/**
683
* A popup window positioned relative to its parent.
684
*/
685
@Composable
686
fun Popup(
687
alignment: Alignment = Alignment.TopStart,
688
offset: IntOffset = IntOffset.Zero,
689
onDismissRequest: (() -> Unit)? = null,
690
properties: PopupProperties = PopupProperties(),
691
content: @Composable () -> Unit
692
)
693
694
/**
695
* Properties for configuring popup behavior.
696
*/
697
@Immutable
698
data class PopupProperties(
699
val focusable: Boolean = false,
700
val dismissOnBackPress: Boolean = true,
701
val dismissOnClickOutside: Boolean = true,
702
val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
703
val excludeFromSystemGesture: Boolean = true,
704
val clippingEnabled: Boolean = true,
705
val usePlatformDefaultWidth: Boolean = false
706
)
707
708
/**
709
* Desktop-specific file dialogs.
710
*/
711
object FileDialog {
712
/**
713
* Show a file chooser dialog.
714
*/
715
suspend fun openFileDialog(
716
title: String = "Open File",
717
allowedExtensions: List<String> = emptyList(),
718
allowMultiSelection: Boolean = false,
719
initialDirectory: String? = null
720
): List<String>?
721
722
/**
723
* Show a save file dialog.
724
*/
725
suspend fun saveFileDialog(
726
title: String = "Save File",
727
allowedExtensions: List<String> = emptyList(),
728
initialFileName: String? = null,
729
initialDirectory: String? = null
730
): String?
731
732
/**
733
* Show a directory chooser dialog.
734
*/
735
suspend fun openDirectoryDialog(
736
title: String = "Choose Directory",
737
initialDirectory: String? = null
738
): String?
739
}
740
```
741
742
**Usage Examples:**
743
744
```kotlin
745
// Basic dialog usage
746
@Composable
747
fun DialogExample() {
748
var showDialog by remember { mutableStateOf(false) }
749
750
Column {
751
Button(onClick = { showDialog = true }) {
752
Text("Show Dialog")
753
}
754
755
if (showDialog) {
756
Dialog(
757
onDismissRequest = { showDialog = false },
758
properties = DialogProperties(
759
dismissOnBackPress = true,
760
dismissOnClickOutside = true
761
)
762
) {
763
Card(
764
modifier = Modifier
765
.fillMaxWidth()
766
.padding(16.dp),
767
shape = RoundedCornerShape(16.dp)
768
) {
769
Column(
770
modifier = Modifier.padding(24.dp),
771
verticalArrangement = Arrangement.spacedBy(16.dp)
772
) {
773
Text(
774
text = "Dialog Title",
775
style = MaterialTheme.typography.h6
776
)
777
778
Text("This is the dialog content.")
779
780
Row(
781
modifier = Modifier.fillMaxWidth(),
782
horizontalArrangement = Arrangement.End
783
) {
784
TextButton(onClick = { showDialog = false }) {
785
Text("Cancel")
786
}
787
TextButton(onClick = { showDialog = false }) {
788
Text("OK")
789
}
790
}
791
}
792
}
793
}
794
}
795
}
796
}
797
798
// Popup window example
799
@Composable
800
fun PopupExample() {
801
var showPopup by remember { mutableStateOf(false) }
802
803
Box {
804
Button(onClick = { showPopup = !showPopup }) {
805
Text("Toggle Popup")
806
}
807
808
if (showPopup) {
809
Popup(
810
alignment = Alignment.TopEnd,
811
offset = IntOffset(0, 40),
812
onDismissRequest = { showPopup = false }
813
) {
814
Card(
815
modifier = Modifier.size(200.dp, 150.dp),
816
elevation = 8.dp
817
) {
818
Column(
819
modifier = Modifier.padding(16.dp),
820
verticalArrangement = Arrangement.spacedBy(8.dp)
821
) {
822
Text("Popup Content")
823
Text("Click outside to dismiss")
824
Button(onClick = { showPopup = false }) {
825
Text("Close")
826
}
827
}
828
}
829
}
830
}
831
}
832
}
833
834
// File dialog example (Desktop)
835
@Composable
836
fun FileDialogExample() {
837
var selectedFiles by remember { mutableStateOf<List<String>>(emptyList()) }
838
var saveLocation by remember { mutableStateOf<String?>(null) }
839
val coroutineScope = rememberCoroutineScope()
840
841
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
842
Button(onClick = {
843
coroutineScope.launch {
844
val files = FileDialog.openFileDialog(
845
title = "Select Images",
846
allowedExtensions = listOf("jpg", "png", "gif"),
847
allowMultiSelection = true
848
)
849
if (files != null) {
850
selectedFiles = files
851
}
852
}
853
}) {
854
Text("Open Files")
855
}
856
857
if (selectedFiles.isNotEmpty()) {
858
Text("Selected files:")
859
selectedFiles.forEach { file ->
860
Text("• $file", style = MaterialTheme.typography.body2)
861
}
862
}
863
864
Button(onClick = {
865
coroutineScope.launch {
866
val location = FileDialog.saveFileDialog(
867
title = "Save Document",
868
allowedExtensions = listOf("txt", "md"),
869
initialFileName = "document.txt"
870
)
871
saveLocation = location
872
}
873
}) {
874
Text("Save File")
875
}
876
877
saveLocation?.let { location ->
878
Text("Save location: $location")
879
}
880
881
Button(onClick = {
882
coroutineScope.launch {
883
val directory = FileDialog.openDirectoryDialog(
884
title = "Choose Output Directory"
885
)
886
if (directory != null) {
887
// Handle directory selection
888
}
889
}
890
}) {
891
Text("Choose Directory")
892
}
893
}
894
}
895
```