From eaf671acb6c3584832683cee391d812b1595a4d1 Mon Sep 17 00:00:00 2001 From: Alexander Maryanovsky Date: Mon, 19 Feb 2024 13:44:07 +0200 Subject: [PATCH] Add an `alwaysOnTop` flag to `DialogWindow`. --- compose/ui/ui/api/desktop/ui.api | 3 +- .../compose/ui/window/Dialog.desktop.kt | 43 +++++++++++++++++++ .../compose/ui/window/DialogWindowTest.kt | 25 +++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/compose/ui/ui/api/desktop/ui.api b/compose/ui/ui/api/desktop/ui.api index c6f38c011f1d0..9224ba522173c 100644 --- a/compose/ui/ui/api/desktop/ui.api +++ b/compose/ui/ui/api/desktop/ui.api @@ -3898,7 +3898,8 @@ public abstract interface class androidx/compose/ui/window/DialogWindowScope : a public final class androidx/compose/ui/window/Dialog_desktopKt { public static final fun Dialog (Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/window/DialogState;ZLjava/lang/String;Landroidx/compose/ui/graphics/painter/Painter;ZZZZZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V public static final fun Dialog (ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V - public static final fun DialogWindow (Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/window/DialogState;ZLjava/lang/String;Landroidx/compose/ui/graphics/painter/Painter;ZZZZZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V + public static final synthetic fun DialogWindow (Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/window/DialogState;ZLjava/lang/String;Landroidx/compose/ui/graphics/painter/Painter;ZZZZZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V + public static final fun DialogWindow (Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/window/DialogState;ZLjava/lang/String;Landroidx/compose/ui/graphics/painter/Painter;ZZZZZZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;III)V public static final fun DialogWindow (ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V } diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Dialog.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Dialog.desktop.kt index 6f911a52b433a..f6e87529d7a53 100644 --- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Dialog.desktop.kt +++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/window/Dialog.desktop.kt @@ -77,11 +77,50 @@ fun Dialog( resizable, enabled, focusable, + alwaysOnTop = false, onPreviewKeyEvent, onKeyEvent, content ) +@Deprecated( + level = DeprecationLevel.HIDDEN, + message = "Replaced by an overload that also takes alwaysOnTop", +) +@Composable +fun DialogWindow( + onCloseRequest: () -> Unit, + state: DialogState = rememberDialogState(), + visible: Boolean = true, + title: String = "Untitled", + icon: Painter? = null, + undecorated: Boolean = false, + transparent: Boolean = false, + resizable: Boolean = true, + enabled: Boolean = true, + focusable: Boolean = true, + onPreviewKeyEvent: ((KeyEvent) -> Boolean) = { false }, + onKeyEvent: ((KeyEvent) -> Boolean) = { false }, + content: @Composable DialogWindowScope.() -> Unit +) { + DialogWindow( + onCloseRequest, + state, + visible, + title, + icon, + undecorated, + transparent, + resizable, + enabled, + focusable, + alwaysOnTop = false, + onPreviewKeyEvent, + onKeyEvent, + content + ) +} + /** * Composes platform dialog in the current composition. When Dialog enters the composition, * a new platform dialog will be created and receives the focus. When Dialog leaves the @@ -129,6 +168,7 @@ fun Dialog( * changing [state]) * @param enabled Can dialog react to input events * @param focusable Can dialog receive focus + * @param alwaysOnTop Should the dialog always be on top of another windows and dialogs * @param onPreviewKeyEvent This callback is invoked when the user interacts with the hardware * keyboard. It gives ancestors of a focused component the chance to intercept a [KeyEvent]. * Return true to stop propagation of this event. If you return false, the key event will be @@ -151,6 +191,7 @@ fun DialogWindow( resizable: Boolean = true, enabled: Boolean = true, focusable: Boolean = true, + alwaysOnTop: Boolean = false, onPreviewKeyEvent: ((KeyEvent) -> Boolean) = { false }, onKeyEvent: ((KeyEvent) -> Boolean) = { false }, content: @Composable DialogWindowScope.() -> Unit @@ -165,6 +206,7 @@ fun DialogWindow( val currentResizable by rememberUpdatedState(resizable) val currentEnabled by rememberUpdatedState(enabled) val currentFocusable by rememberUpdatedState(focusable) + val currentAlwaysOnTop by rememberUpdatedState(alwaysOnTop) val currentOnCloseRequest by rememberUpdatedState(onCloseRequest) val updater = remember(::ComponentUpdater) @@ -243,6 +285,7 @@ fun DialogWindow( set(currentResizable, dialog::setResizable) set(currentEnabled, dialog::setEnabled) set(currentFocusable, dialog::setFocusableWindowState) + set(currentAlwaysOnTop, dialog::setAlwaysOnTop) } if (state.size != appliedState.size) { dialog.setSizeSafely(state.size, WindowPlacement.Floating) diff --git a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/DialogWindowTest.kt b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/DialogWindowTest.kt index 11f1541b5bab4..e5ec1af755356 100644 --- a/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/DialogWindowTest.kt +++ b/compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/window/DialogWindowTest.kt @@ -31,6 +31,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.awt.ComposeDialog +import androidx.compose.ui.awt.ComposeWindow import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusTarget @@ -642,4 +643,28 @@ class DialogWindowTest { assertThat(renderedText).isEqualTo("2") } } + + @Test + fun `change alwaysOnTop`() = runApplicationTest { + var dialog: ComposeDialog? = null + + var alwaysOnTop by mutableStateOf(false) + + launchTestApplication { + DialogWindow(onCloseRequest = ::exitApplication, alwaysOnTop = alwaysOnTop) { + dialog = this.window + Box(Modifier.size(32.dp).background(Color.Red)) + } + } + + awaitIdle() + assertThat(dialog?.isAlwaysOnTop).isFalse() + + alwaysOnTop = true + awaitIdle() + assertThat(dialog?.isAlwaysOnTop).isTrue() + + dialog?.dispatchEvent(WindowEvent(dialog, WindowEvent.WINDOW_CLOSING)) + } + } \ No newline at end of file