Spec-Zone.ru › Kotlin 2

Управление окнами верхнего уровня

Compose Multiplatform для настольных компьютеров предоставляет различные возможности для управления окнами. Вы можете скрывать окна в системном трее, делать их перетаскиваемыми, менять их размер и положение и многое другое.

См. также новую экспериментальную версию 2 API окон и диалоговых окон.

Примеры в этом руководстве — запускаемые настольные приложения. Чтобы попробовать один из них:

  1. Создайте проект Compose Multiplatform с помощью плагина Kotlin Multiplatform для IDE или онлайн-мастера.

  2. Откройте файл точки входа настольного приложения. Например, desktopApp/src/main/kotlin/com/example/my_desktop_app/main.kt.

  3. Замените его содержимое приведённым примером.

Открытие и закрытие окон

Чтобы создать обычное окно, можно использовать функцию Window(). Чтобы поместить её в область видимости composable-функции, используйте Window() в точке входа application:

import androidx.compose.ui.window.Window import androidx.compose.ui.window.application fun main() = application { Window(onCloseRequest = ::exitApplication) { // Content of the window } }

Как composable-функция, Window() позволяет декларативно изменять её свойства. Например, можно открыть окно с одним заголовком, а затем изменить его:

import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.window.Window import androidx.compose.ui.window.application fun main() = application { var fileName by remember { mutableStateOf("Untitled") } Window(onCloseRequest = ::exitApplication, title = "$fileName - Editor") { Button(onClick = { fileName = "note.txt" }) { Text("Save") } } }
Window properties: change title

Добавление условий

Открывать и закрывать окна можно также с помощью простых условий if. В следующем примере кода окно приложения автоматически закрывается после выполнения задачи:

import androidx.compose.material.Text import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import kotlinx.coroutines.delay fun main() = application { var isPerformingTask by remember { mutableStateOf(true) } LaunchedEffect(Unit) { // Do some heavy lifting delay(2000) isPerformingTask = false } if (isPerformingTask) { Window( onCloseRequest = ::exitApplication, title = "Window 1" ) { Text("Performing some tasks. Please wait!") } } else { Window( onCloseRequest = ::exitApplication, title = "Window 2" ) { Text("Hello, World!") } } }
Windows with conditions

Запрос подтверждения при закрытии

Если при выходе из приложения нужно выполнить пользовательскую логику, например показать диалоговое окно, можно переопределить действие закрытия с помощью обратного вызова onCloseRequest. В следующем примере вместо императивного подхода (window.close()) используется декларативный: окно закрывается в ответ на изменение состояния (isOpen = false).

import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.window.DialogWindow import androidx.compose.ui.window.Window import androidx.compose.ui.window.application fun main() = application { var isOpen by remember { mutableStateOf(true) } var isAskingToClose by remember { mutableStateOf(false) } if (isOpen) { Window( onCloseRequest = { isAskingToClose = true }, title = "Important document" ) { if (isAskingToClose) { DialogWindow( onCloseRequest = { isAskingToClose = false }, title = "Close without saving?" ) { Button( onClick = { isOpen = false } ) { Text("Yes") } } } } } }
Close with confirmation

Создание одноконного приложения

Для простого приложения с одним окном верхнего уровня не нужна полноценная точка входа application с composable-функцией Window() — функция singleWindowApplication() объединяет их в одном вызове:

import androidx.compose.ui.window.singleWindowApplication

fun main() = singleWindowApplication {
    // Content of the window
}

Если требуется несколько окон верхнего уровня, пользовательская логика закрытия или изменение атрибутов окна во время выполнения, используйте composable-функцию Window() в точке входа application.

Управление состоянием окна

Класс WindowState хранит сведения о размещении окна, его текущем положении и размере. Атрибут размещения позволяет указать, как окно располагается на экране: в обычном режиме, развёрнутым/свёрнутым или в полноэкранном режиме. Любое изменение состояния запускает автоматическую рекомпозицию. Чтобы изменить состояние окна, используйте обратные вызовы или отслеживайте его в composable-функциях:

import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.material.Checkbox import androidx.compose.material.Text import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowPlacement import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState fun main() = application { val state = rememberWindowState(placement = WindowPlacement.Maximized) Window(onCloseRequest = ::exitApplication, state, title = "Window state") { Column { Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( state.placement == WindowPlacement.Fullscreen, { state.placement = if (it) { WindowPlacement.Fullscreen } else { WindowPlacement.Floating } } ) Text("isFullscreen") } Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( state.placement == WindowPlacement.Maximized, { state.placement = if (it) { WindowPlacement.Maximized } else { WindowPlacement.Floating } } ) Text("isMaximized") } Row(verticalAlignment = Alignment.CenterVertically) { Checkbox(state.isMinimized, { state.isMinimized = !state.isMinimized }) Text("isMinimized") } Text( "Position ${state.position}", Modifier.clickable { val position = state.position if (position is WindowPosition.Absolute) { state.position = position.copy(x = state.position.x + 10.dp) } } ) Text( "Size ${state.size}", Modifier.clickable { state.size = state.size.copy(width = state.size.width + 10.dp) } ) } } }
Changing the state

Подгонка размера окна под его содержимое

Чтобы задать размер окна в соответствии с его содержимым, не указывая размеры заранее, задайте для одного или обоих измерений окна значение Dp.Unspecified. Compose Multiplatform автоматически подберёт исходный размер окна по его содержимому:

import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState fun main() = application { Window( onCloseRequest = ::exitApplication, state = rememberWindowState(width = Dp.Unspecified, height = Dp.Unspecified), title = "Adaptive size", resizable = false ) { Column(Modifier.background(Color(0xFFEEEEEE))) { Row { Text("label 1", Modifier.size(100.dp, 100.dp).padding(10.dp).background(Color.White)) Text("label 2", Modifier.size(150.dp, 200.dp).padding(5.dp).background(Color.White)) Text("label 3", Modifier.size(200.dp, 300.dp).padding(25.dp).background(Color.White)) } } } }
Adaptive window size

Отслеживание изменений состояния

Чтобы реагировать на изменения состояния и передавать значение в часть приложения, не являющуюся composable-функцией (например, для записи в базу данных), можно использовать функцию snapshotFlow(). Эта функция фиксирует текущее значение состояния composable-функции.

import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.unit.DpSize import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach fun main() = application { val state = rememberWindowState() Window(onCloseRequest = ::exitApplication, state) { LaunchedEffect(state) { snapshotFlow { state.size } .onEach(::onWindowResize) .launchIn(this) snapshotFlow { state.position } .filter { it.isSpecified } .onEach(::onWindowRelocate) .launchIn(this) } } } private fun onWindowResize(size: DpSize) { println("onWindowResize $size") } private fun onWindowRelocate(position: WindowPosition) { println("onWindowRelocate $position") }

Управление несколькими окнами

Для управления несколькими окнами можно создать отдельный класс для состояния приложения и открывать или закрывать окна в ответ на изменения mutableStateListOf:

import androidx.compose.runtime.Composable import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.window.MenuBar import androidx.compose.ui.window.Window import androidx.compose.ui.window.application fun main() = application { val applicationState = remember { MyApplicationState() } for (window in applicationState.windows) { key(window) { MyWindow(window) } } } @Composable private fun MyWindow( state: MyWindowState ) = Window(onCloseRequest = state::close, title = state.title) { MenuBar { Menu("File") { Item("New window", onClick = state.openNewWindow) Item("Exit", onClick = state.exit) } } } private class MyApplicationState { val windows = mutableStateListOf<MyWindowState>() init { windows += MyWindowState("Initial window") } fun openNewWindow() { windows += MyWindowState("Window ${windows.size}") } fun exit() { windows.clear() } private fun MyWindowState( title: String ) = MyWindowState( title, openNewWindow = ::openNewWindow, exit = ::exit, windows::remove ) } private class MyWindowState( val title: String, val openNewWindow: () -> Unit, val exit: () -> Unit, private val close: (MyWindowState) -> Unit ) { fun close() = close(this) }
Multiple windows

Более сложный пример см. в примере Code Viewer.

Отображение диалоговых окон

С помощью composable-функции DialogWindow() можно отображать отдельное окно уровня ОС с собственной строкой заголовка. Это удобно для подтверждений, выбора файлов или любого взаимодействия, которое пользователь должен завершить, прежде чем продолжить работу.

С помощью экспериментального параметра modalityType можно управлять тем, блокирует ли диалоговое окно взаимодействие с другими окнами. Задайте одно из значений DialogModalityType:

  • Modeless не блокирует другие окна.

  • DocumentModal блокирует родительское окно верхнего уровня и все связанные с ним окна, кроме дочерних окон диалогового окна.

  • ApplicationModal блокирует все остальные окна в том же приложении.

Для наложенного интерфейса внутри текущего окна (выпадающих списков, всплывающих подсказок и пользовательских наложений) используйте мультиплатформенную composable-функцию Popup().

В следующем примере кода обычное окно объединено с диалоговым окном ApplicationModal:

import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Button import androidx.compose.material.Text 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.window.DialogWindow import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberDialogState import androidx.compose.ui.window.DialogModalityType import androidx.compose.ui.ExperimentalComposeUiApi // Enables experimental modalityType @OptIn(ExperimentalComposeUiApi::class) fun main() = application { Window( onCloseRequest = ::exitApplication, title = "Main window" ) { var isDialogOpen by remember { mutableStateOf(false) } Button(onClick = { isDialogOpen = true }) { Text(text = "Open dialog") } if (isDialogOpen) { DialogWindow( onCloseRequest = { isDialogOpen = false }, state = rememberDialogState(position = WindowPosition(Alignment.Center)), title = "Dialog", modalityType = DialogModalityType.ApplicationModal ) { Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { Text("This is a dialog") } } } } }

Скрытие окон в системный трей

По умолчанию закрытие окна завершает приложение. Чтобы вместо этого скрыть окно в системный трей или строку меню, можно перехватить onCloseRequest и изменить состояние видимости окна.

В следующем примере при закрытии окна значение isVisible устанавливается в false, в результате чего окно скрывается и отображается значок в системном трее. Нажатие на значок восстанавливает окно.

import androidx.compose.material.Text import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.window.Tray import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import kotlinx.coroutines.delay fun main() = application { var isVisible by remember { mutableStateOf(true) } Window( // Hides the window instead of closing the app onCloseRequest = { isVisible = false }, visible = isVisible, title = "Counter", ) { var counter by remember { mutableStateOf(0) } LaunchedEffect(Unit) { while (true) { counter++ delay(1000) } } Text(counter.toString()) } if (!isVisible) { Tray( TrayIcon, tooltip = "Counter", onAction = { isVisible = true }, menu = { Item("Exit", onClick = ::exitApplication) }, ) } } object TrayIcon : Painter() { override val intrinsicSize = Size(256f, 256f) override fun DrawScope.onDraw() { drawOval(Color(0xFFFFA500)) } }
Hide instead of closing

Создание перетаскиваемых областей окна

Чтобы добавить пользовательскую перетаскиваемую строку заголовка в окно без оформления или сделать перетаскиваемым всё окно, можно использовать composable-функцию WindowDraggableArea():

import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.window.WindowDraggableArea import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application fun main() = application { Window(onCloseRequest = ::exitApplication, undecorated = true) { WindowDraggableArea { Box(Modifier.fillMaxWidth().height(48.dp).background(Color.DarkGray)) } } }

WindowDraggableArea() можно использовать только внутри composable-функций singleWindowApplication(), Window() и DialogWindow(). Чтобы вызвать её в другой composable-функции, используйте WindowScope в качестве области-получателя:

import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.window.WindowDraggableArea import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowScope import androidx.compose.ui.window.application fun main() = application { Window(onCloseRequest = ::exitApplication, undecorated = true) { AppWindowTitleBar() } } @Composable private fun WindowScope.AppWindowTitleBar() = WindowDraggableArea { Box(Modifier.fillMaxWidth().height(48.dp).background(Color.DarkGray)) }
Draggable area

Создание прозрачных окон

Чтобы создать прозрачное окно, передайте функции Window() два параметра: transparent=true и undecorated=true. Окно должно быть без оформления, поскольку оформить прозрачное окно невозможно.

В следующем примере кода показано, как объединить composable-функции для создания прозрачного окна с закруглёнными углами:

import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Surface import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.material.Text import androidx.compose.runtime.* fun main() = application { var isOpen by remember { mutableStateOf(true) } if (isOpen) { Window( onCloseRequest = { isOpen = false }, title = "Transparent Window Example", transparent = true, // Transparent window must be undecorated undecorated = true, ) { Surface( modifier = Modifier.fillMaxSize().padding(5.dp).shadow(3.dp, RoundedCornerShape(20.dp)), color = Color.Transparent, // Window with rounded corners shape = RoundedCornerShape(20.dp) ) { Text("Hello World!", color = Color.White) } } } }

Использование компонентов Swing

Compose Multiplatform для настольных компьютеров использует Swing под капотом, поэтому окно можно создать напрямую с помощью Swing:

import androidx.compose.ui.awt.ComposeWindow import java.awt.Dimension import javax.swing.JFrame import javax.swing.SwingUtilities fun main() = SwingUtilities.invokeLater { ComposeWindow().apply { size = Dimension(300, 300) defaultCloseOperation = JFrame.DISPOSE_ON_CLOSE setContent { // Content of the window } isVisible = true } }

Также можно использовать область видимости composable-функции Window(). В следующем примере кода window — это ComposeWindow, созданный внутри Window():

import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.window.singleWindowApplication import java.awt.datatransfer.DataFlavor import java.awt.dnd.DnDConstants import java.awt.dnd.DropTarget import java.awt.dnd.DropTargetAdapter import java.awt.dnd.DropTargetDropEvent fun main() = singleWindowApplication { LaunchedEffect(Unit) { window.dropTarget = DropTarget().apply { addDropTargetListener(object : DropTargetAdapter() { override fun drop(event: DropTargetDropEvent) { event.acceptDrop(DnDConstants.ACTION_COPY) val fileName = event.transferable.getTransferData(DataFlavor.javaFileListFlavor) println(fileName) } }) } } }

Если необходимо использовать диалоговое окно, реализованное в Swing, его можно обернуть в composable-функцию:

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.ui.window.AwtWindow import androidx.compose.ui.window.application import java.awt.FileDialog import java.awt.Frame fun main() = application { var isOpen by remember { mutableStateOf(true) } if (isOpen) { FileDialog( onCloseRequest = { isOpen = false println("Result $it") } ) } } @Composable private fun FileDialog( parent: Frame? = null, onCloseRequest: (result: String?) -> Unit ) = AwtWindow( create = { object : FileDialog(parent, "Choose a file", LOAD) { override fun setVisible(value: Boolean) { super.setVisible(value) if (value) { onCloseRequest(file) } } } }, dispose = FileDialog::dispose )

API окон и диалоговых окон v2

Начиная с Compose Multiplatform 1.12.0, в под-пакете androidx.compose.ui.window.v2 доступны переработанные классы WindowState и DialogState.

API окон и диалоговых окон v2 отделяет запрос состояния от наблюдения за состоянием, фактически применённым диспетчером окон. Оно также открывает возможности для сценариев, которые раньше были недоступны, например для задания размера окна в соответствии с предпочтительным размером его содержимого, при этом содержимое по-прежнему может расширяться (с помощью модификаторов, таких как fillMaxSize()), если окно становится больше. Подробнее см. в разделе Задание размера.

API v2 доступно наряду с существующим API, описанным на остальной части этой страницы, поэтому вы можете переносить отдельные окна в удобном для вас темпе.

Задание состояния и наблюдение за ним

API v2 явно отделяет задание желаемого состояния от наблюдения за фактическим состоянием.

Чтобы задать начальное состояние окна, передайте поставщики в rememberWindowState():

import androidx.compose.material.Text import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.application import androidx.compose.ui.window.v2.Window import androidx.compose.ui.window.v2.WindowBoundsProvider import androidx.compose.ui.window.v2.WindowPositionProvider import androidx.compose.ui.window.v2.WindowSizeProvider import androidx.compose.ui.window.v2.rememberWindowState @OptIn(ExperimentalComposeUiApi::class) fun main() = application { val windowState = rememberWindowState( initialBoundsProvider = WindowBoundsProvider( positionProvider = WindowPositionProvider.CenteredOnScreen, sizeProvider = WindowSizeProvider.Fixed(DpSize(400.dp, 200.dp)) ) ) Window( onCloseRequest = ::exitApplication, state = windowState, ) { Text("Hello, World!", fontSize = 48.sp) } }

Чтобы запросить изменение состояния после создания окна, вызовите соответствующий метод у WindowState. Точный размер и положение можно задать напрямую:

windowState.requestScreen { defaultScreen }
windowState.requestSize(DpSize(1024.dp, 768.dp))
windowState.requestPosition(DpOffset(100.dp, 100.dp))

Для относительного позиционирования или динамического вычисления размеров используйте поставщики:

windowState.requestBounds(
    WindowBoundsProvider(
        positionProvider = WindowPositionProvider.CenteredOnScreen,
        sizeProvider = WindowSizeProvider.Fixed(DpSize(1024.dp, 768.dp))
    )
)

Запрос применяется асинхронно. Оконная система может изменить запрошенное состояние, а фактическое состояние может измениться позднее, например при перемещении окна или изменении его размера. Наблюдать за фактическим состоянием окна можно с помощью WindowState.screenId и WindowState.bounds:

if (windowState.isInitialized) {
    Text("Current screen: ${windowState.screenId}")
    Text("Current bounds: ${windowState.bounds}")
}

Та же асинхронная модель доступна для диалоговых окон через DialogState и rememberDialogState().

Выбор экрана

Можно запросить отображение окна на определённом экране, передав initialScreenProvider в rememberWindowState() или вызвав WindowState.requestScreen() позднее. Наблюдать за экраном, на котором фактически размещено окно, можно через WindowState.screenId.

Например, можно запросить размещение окна на экране с доступной шириной не менее 1024.dp, указав экран по умолчанию в качестве запасного варианта:

windowState.requestScreen {
    screens.firstOrNull { it.availableBounds.width >= 1024.dp }
        ?: defaultScreen
}

Задание положения

Чтобы изменить положение окна, передайте initialBoundsProvider в rememberWindowState() или вызовите WindowState.requestBounds() позднее. Наблюдать за фактическими границами окна можно через WindowState.bounds.

API v2 использует WindowPositionProvider для получения информации об экране и геометрии родительского окна.

Для стандартного размещения можно использовать встроенные свойства:

  • Default применяет стандартное поведение каскадного размещения операционной системы.

  • Current сохраняет текущее положение окна.

  • CenteredOnScreen размещает окно по центру экрана.

  • CenteredInParentWindow размещает окно по центру родительского окна.

Для более гибкой настройки используйте функции-поставщики положения:

  • Absolute() размещает начальный угол окна по заданным координатам x и y.

  • AlignedToScreen() выравнивает окно относительно экрана и включает необязательный параметр смещения.

    WindowPositionProvider.AlignedToScreen(
        alignment = Alignment.Center,
        offset = DpOffset(x = 16.dp, y = 16.dp)
    )
    
  • AlignedToParentWindow() привязывает окно к родительскому окну и обычно используется для диалоговых окон.

    WindowPositionProvider.AlignedToParentWindow(
        // Anchors to the starting corner of the parent window
        anchor = Alignment.TopStart,
        // Applies alignment relative to the anchor point
        alignment = Alignment.Center
    )
    

Задание размера

Размер также входит в границы окна, поэтому он настраивается с помощью того же механизма initialBoundsProvider/WindowState.requestBounds().

API v2 использует WindowSizeProvider для получения информации о размерах экрана и родительского окна, а также для запроса у содержимого окна его естественных размеров.

К часто используемым встроенным параметрам относятся Fixed() для задания определённого размера окна и Default для стандартного размера 800×600 dp.

Для настройки размера пользовательская лямбда WindowSizeProvider() получает доступ к параметрам экрана, а для диалоговых окон — к параметрам родительского окна:

WindowSizeProvider {
    val height = parentWindowMetrics!!.bounds.height
    DpSize(300.dp, height)
}

API v2 поддерживает один из часто запрашиваемых сценариев: задание размера окна в соответствии с предпочтительным размером его содержимого с возможностью заполнения содержимым всего окна, если пользователь увеличит его. WindowSizeProvider.Unconstrained вычисляет размер содержимого, добавляет отступы окна и ограничивает результат доступным размером экрана. Поскольку размер задаётся независимо от компоновки, содержимое с fillMaxSize() всё равно расширяется, заполняя окно, если пользователь меняет его размер.

WindowBoundsProvider(
    positionProvider = WindowPositionProvider.CenteredOnScreen,
    sizeProvider = WindowSizeProvider.Unconstrained
)

Версии компонуемых функций Window() и DialogWindow() для API v2 принимают параметры minSize и maxSize. Если базовый диспетчер окон поддерживает такую возможность, пользователь не сможет изменить размер окна за пределы этих границ:

DialogWindow(
    onCloseRequest = { showDialog = false },
    state = dialogState,
    minSize = DpSize(250.dp, 250.dp),
    maxSize = DpSize(500.dp, 500.dp)
) {
    // ...
}

Что дальше

Изучите руководства по другим компонентам для настольных приложений.

25 августа 2026 г.
Изображения и значки в приложенииПолосы прокрутки

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/multiplatform/compose-desktop-top-level-windows-management.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API